我5月份找實習的時候被百度面試的一個問題,當時確實答了一半,還有一半沒答上來,總結在了筆記本上了,現在拿出來整理了一下,詳細拿出來說一下。
問題一:
??在button設置一個onTouchListener,設置一個onClickListener,在onTouch方法里根據動作輸出對應的up,down動作,在onClick里邊輸出click。點擊按鈕,問輸出的順序。
問題二:
???如果把OnClickListener換成OnLongClickListener,長按按鈕再松開,打印什么結果順序?
???看到上面的問題,第一個問題相信很多人都能一次性答對,但是第二個問題就不一定了是吧,如果不是面試被問到,估計很難意識到這個問題,這種需要對比記憶理解的我覺得還是很有必要拿出來和大家分享一下的。
???好了,回到正題,回答問題
問題一:來,直接上個例子吧,先看結果。
如上代碼所示,定義了一個button,設置了touch和click兩個點擊事件,onTouch返回的是默認的false。在點擊事件里打了Log方便看結果
很簡單的布局,就不說明什么了哈。
點擊一下按鈕:結果如下
說明是先打印down,再打印UP,UP以后才發生click,這個從View事件分發的dispatchTouchEvent源碼可以分析出來,相信再簡單不過了。
延伸1:如果把onTouch返回true,結果會是怎樣?
再點擊一下按鈕,結果圖如下:
???相信不用解釋了吧,dispatchTouchEvent里邊的if語句直接滿足三個條件,所以函數直接返回了true,消費了事件,所以不會走view的onTouchEvent嗎,自然不會觸發click事件。
問題二:
把OnClickListener換成OnLongClickListener
先解釋一下這個函數吧,
public boolean onLongClick(View v)
???參數v:參數v為事件源控件,當長時間按下此控件時才會觸發該方法。
???返回值:該方法的返回值為一個boolean類型的變量,當返回true時,表示已經完整地處理了這個事件,并不希望其他的回調方法再次進行處理;當返回false時,表示并沒有完全處理完該事件,更希望其他方法繼續對其進行處理。
???直接上代碼圖
點擊一下按鈕,看結果
長按按鈕別松開,看結果:
這時候松開看結果:
把onLongClick中的方法改成返回true;長按一會再松開。
依舊打印一樣的結果。
OnLongClickListener的事件流程:
結論:
長按的調用棧:
onTouchEvent –case:ACTION_DOWN-checkForLongClick –post-
CheckForLongPress--run—performLongClick-.mOnLongClickListener.onLongClick
走一遍函數吧:
在onTouchEvent的MotionEvent.ACTION_DOWN,執行checkForLongClick
看到了吧,最后還是調用了我們設置的longclick點擊事件。回顧一下,是在case的ACTION_DOWN分支中運行的這個點擊。
所以最后點擊了mOnLongClickListener的onLongClick
??延伸2:onClick和onLongClick能同時發生嗎?
??要理解Android對事件處理的所謂消費(consume)概念即可,一個用戶的操作會被傳遞到不同的View控件和同一個控件的不同監聽方法處理,任何一個接收并處理了該次事件的方法如果在處理完后返回了true,那么該次event就算被完全處理了,其他的View或者監聽方法就不會再有機會處理該event了。
?? onLongClick的發生是由單獨的線程完成的,一般發生在ACTION_UP之前,而onClick的發生是在ACTION_UP后。臨界條件是同時發生,這里會有flag來進行區分。
?? 因此同一次用戶touch操作就有可能既發生onLongClick又發生onClick。
及時向系統表示“我已經完全處理(消費)了用戶的此次操作”,是很重要的事情。
另外一個同時執行的概念(都執行的意思),先后執行,例如,我們如果在onLongClick()方法的最后return true,那么onClick事件就沒有機會被觸發了
在onLongClick()方法return false的情況下,會有一次觸碰操作的基本時序。
??如下問題3:
把onClick和onLongClick同時寫入代碼。
??可以看到我們寫了ontouch,onclick,onlongclick三個點擊。并且兩個有個返回值的返回都是false。
??還是這個界面
-
點擊一下按鈕,結果:
image.png
這個結果沒啥解釋的吧,就是view基本的事件傳遞模型。在UP分支中執行了
image.png
在它里邊執行了li.mOnClickListener.onClick(this); - 長按一會再松開,結果:
從DOWN到UP,可以看出來前后發生了什么
??可以看到,在ACTION_UP后仍然觸發了onClick()方法。onLongClick一般發生在ACTION_UP之前,而onClick的發生是在ACTION_UP后。
??如果此時把longClick返回true,其他代碼不變。
再長按一下按鈕松開,則打印:
發現不觸發onClick了。當longclick把事件消費了以后,那么onClick事件就沒有機會被觸發了。
好了,寫了這么多其實是個特別簡單的問題,但是覺得還是應該分享一下,畢竟積少成多嘛,加油吧!騷年