通俗理解Android事件分發與消費機制


深入:Android Touch事件傳遞機制全面解析(從WMS到View樹)

 

通俗理解Android事件分發與消費機制

  說起Android滑動沖突,是個很常見的場景,比如SliddingMenu與ListView的嵌套,要解決滑動沖突,不得不提及到View的事件分發機制。


  Touch事件傳遞規則分析
首先,我們要知道Touch事件是包裝在MotionEvent對象中的,在手指與屏幕接觸過程中產生一系列事件,典型的事件有以下三種:
ACTION_DOWN:手指剛接觸屏幕的瞬間

ACTION_MOVE:手指在屏幕上滑動

ACTION_UP:手指剛離開屏幕的瞬間

 

那么,Android中Touch事件是一個怎樣的傳遞過程呢?

 

 1 , 事件分發:public boolean dispatchTouchEvent(MotionEvent ev)

Touch事件發生時Activity的dispatchTouchEvent(MotionEvent ev)方法會將事件傳遞給最外層View的dispatchTouchEvent(MotionEvent ev)方法,該方法對事件進行分發。分發邏輯如下:
    如果return true,事件會由當前View的dispatchTouchEvent方法進行消費,同時事件會停止向下傳遞;

    如果return false,事件分發分為兩種情況:
      如果當前 View 獲取的事件直接來自 Activity,則會將事件返回給Activity的onTouchEvent進行消費;
      如果當前 View 獲取的事件來自外層父控件,則會將事件返回給父View的onTouchEvent進行消費。

    如果return super.dispatchTouchEvent(ev),事件分發分為兩種情況:

     如果當前View是ViewGroup,則事件會分發給onInterceptTouchEvent方法進行處理;

     如果當前View是普通View,則事件直接交給onTouchEvent方法進行處理

   2, 事件攔截:public boolean onInterceptTouchEvent(MotionEvent ev)

此方法只有ViewGroup才有, Activity與普通View沒有。上面已經提到,如果當前ViewGroup的dispatchTouchEvent(事件分發)返回super.dispatchTouchEvent(ev), 那么事件會傳遞到傳遞到onInterceptTouchEvent方法, 該方法對事件進行攔截。攔截邏輯如下:
    如果return true,則表示攔截該事件,並將事件交給當前View的onTouchEvent方法;

    如果return false,則表示不攔截該事件,並將該事件交由子View的dispatchTouchEvent方法進行事件分發,重復上述過程;

    如果return super.onInterceptTouchEvent(ev), 事件攔截分兩種情況:       

       如果該View(ViewGroup)存在子View且點擊到了該子View, 則不攔截, 繼續分發給子View 處理, 此時相當於return false。

     如果該View(ViewGroup)沒有子View或者有子View但是沒有點擊中子View(此時ViewGroup相當於普通View), 則交由該View的onTouchEvent響應,此時相當於return true。 

一般的LinearLayout、 RelativeLayout、FrameLayout等ViewGroup默認不攔截, 而ScrollView、ListView等ViewGroup則可能攔截,得看具體情況。

   3, 事件響應:public boolean onTouchEvent(MotionEvent ev)

上面已經提到,在dispatchTouchEvent(事件分發)返回super.dispatchTouchEvent(ev)並且onInterceptTouchEvent進行攔截(事件攔截返回true)的情況下,那么事件會傳遞到onTouchEvent方法,該方法對事件進行響應。響應邏輯如下:
    如果return true,則表示響應並消費該事件;
    如果return fasle,則表示不響應事件,那么該事件將會不斷向上層View的onTouchEvent方法傳遞,直到某個View的onTouchEvent方法返回true,如果到了最頂層View還是返回false,那么認為該事件不消耗,則在同一個事件系列中,當前View無法再次接收到事件,該事件會交由Activity的onTouchEvent進行處理;
    如果return super.dispatchTouchEvent(ev),事件處理分為兩種情況:

    如果該View是clickable或者longclickable的,則會返回true, 表示消費了該事件, 與返回true一樣;

    如果該View不是clickable或者longclickable的,則會返回false, 表示不消費該事件,將會向上傳遞,與返回false一樣.

 

上述三個方法到底有什么區別與聯系呢?我們通過一段偽代碼來表示:

 事件分發偽代碼

[java]  view plain  copy
 
  1. public boolean dispatchTouchEvent(MotionEvent ev){  
  2.     boolean consume = false;  
  3.     if(onInterceptTouchEvent(ev)){ // 如果onInterceptTouchEvent返回true  
  4.         consume = onTouchEvent(ev);  // 則交由該View的onTouchEvent方法  
  5.     } else {  
  6.         consume = child. dispatchTouchEvent(ev); // 否則交由子View的dispatchTouchEvent事件進行分發  
  7.     }  
  8.     return consume; // 如果成功消費該事件,則返回true,然后停止傳遞,否則返回false  
  9. }  

 -------------------------------------------------------------------分割線------------------------------------------------------------

各組件對應方法的有無情況:

 注: Activity的dispatchTouchEvent最終是調用了Window對應DecorView的dispatchTouchEvent, 相當於ViewGroup; onTouchEvent是Activity自帶的方法並不是DecorView的onTouchEvent; 同時,沒有onInterceptTouchEvent方法是因為Window並沒有回調該方法。

 

返回值作用:true和false標志事件是否被消費。

如果消費了就不再傳遞給其他控件了,如果沒有消費則還會傳遞給父控件或者子控件,觸發相應控件的事件處理函數。

 

控件默認返回值

1,對於ViewGroup的onInterceptTouchEvent方法:

  如果存在子View且點擊到了子View, 則不攔截, 繼續分發給子View 處理, 此時返回super.onInterceptTouchEvent(ev) 就相當於return false。

  如果沒有子View或者有子View但是沒有點擊中子View(此時ViewGroup相當於普通View), 則交由當前View的onTouchEvent響應,此時返回super.onInterceptTouchEvent(ev) 相當於return true。 

2,對於View的onTouchEvent方法: 如果是clickable或者longClickable的,則返回true消費該事件; 否則返回false不消費該事件,從而往上傳遞.

 

 -------------------------------------------------------------------分割線------------------------------------------------------------

 

 注:同一個事件序列是指從手指接觸屏幕的那一刻開始,到手指離開屏幕那一刻結束,在這過程中所產生的一系列事件,這個事件序列以down事件開始,以up事件結束,中間含有數量不定的move事件.

事件分發與消費的規則總結:   

  (1)事件的分發是以隧道方式由上到下的, 即事件總是先傳遞給父元素, 然后再由父元素分發給子元素。對於onTouchEvent事件,如果返回false,則會以冒泡方式向上傳遞。 頂級View接收到事件之后,就會按相應規則去分發事件。如果一個View的onTouchEvent方法返回false,那么將會交給父容器的onTouchEvent方法進行處理,以冒泡方式逐級往上,如果所有的View都不處理該事件,則交由Activity的onTouchEvent進行處理。就跟工作中遇到了難題,逐級找領導解決一個道理,領導解決不了,再找上一級領導。

    (2)正常情況下,一個事件序列只能被一個View攔截且消耗。某個View一旦進行事件攔截,那么這一個事件序列都只能交由他處理,並且onInterceptTouchEvent也不會被再次調用。因此,正常情況下一個事件是不能交給兩個View來處理的,當然,特殊做法就是在View的onTouchEvent處理完之后再返回false,強行交給其他View處理。

    (3)如果某一個View開始處理事件,如果他不消耗ACTION_DOWN事件(也就是onTouchEvent返回false),則同一事件序列比如接下來進行ACTION_MOVE、ACTION_UP,則不會再交給該View處理,並且事件將重新提交給它的父元素處理。就像工作中做一件事情,你要么做完,要么你就不要做這件事了。

    (4)ViewGroup的onInterceptTouchEvent方法默認返回false,即不攔截任何事件,而交給子View進行分發處理(前提是有子View)。

    (5)普通View(比如TextView、ImageView,非ViewGroup)沒有onInterceptTouchEvent方法, 一旦有事件傳遞給它,它的onTouchEvent方法就會被調用。正常情況下,它們都會消耗事件(返回true),除非它們是不可點擊的(clickable和longClickable都為false),那么就會交由父容器的onTouchEvent處理。View的longClickable默認都是false的,而對於clickable則要分情況,比如Button的clickable默認你是true,而TextView默認是false.

    (6)如果View不消耗除down以外的其他事件, 此時父View的onTouchEvent並不會被調用, 並且當前View可以持續收到后續事件,最終這些事件會傳遞給Activity處理.

  (7)View的enable屬性不影響onTouchEvent的默認返回值,只要它clickable或者longClickable為true,則onTouchEvent就會返回true。

  (8)  如果當前View是可點擊的,並且它收到了down和up事件(以down開始,以up結束),則它的click事件就會觸發;對於onLongClick,則只要當前View是longClickable的並接收到down事件且超過了系統默認的long時間,則就會觸發,只與down事件有關而與up事件無關.

    (9)點擊事件分發過程如下 dispatchTouchEvent—->OnTouchListener的onTouch方法—->onTouchEvent-->OnClickListener的onClick方法。也就是說,我們平時調用的setOnClickListener,優先級是最低的,所以,OnTouchListener的onTouch方法如果返回true,則不響應onClick方法...

  (10) 子View可以通過requestDisallowInterceptTouchEvent方法請求父控件不要攔截事件,從而干預事件的分發過程,但是down事件除外,無法干預到

 (11)如果一個View監聽了onTouch,則在onTouch里面應該返回false,否則onTouchEvent事件及點擊、長按事件就無法監聽到

   (12)如果ViewGroup中的子View將傳遞的事件消費掉,ViewGroup的onTouch將無法接收到任何事件, 但onTouchEvent還是能接收到的;  如果是View的onTouchEvent消費,則該View的onTouch仍然能接收到事件,因為此時onTouch的調用在onTouchEvent之前。   總之,對於View, 無論onTouchEvent消費與否,都會觸發View的onTouch事件, 因為onTouch的調用在onTouchEvent之前。

 

 -------------------------------------------------------------------分割線------------------------------------------------------------

【參考資料】

Trinea 

Android Touch事件傳遞機制

郭林

Android事件分發機制完全解析,帶你從源碼的角度徹底理解(上) 

Android事件分發機制完全解析,帶你從源碼的角度徹底理解(下) 

鴻洋

Android  View事件分發機制 源碼解析 (上)

Android ViewGroup事件分發機制 源碼解析(下) 

工匠若水

Android觸摸屏事件派發機制詳解與源碼分析一(View篇)

Android觸摸屏事件派發機制詳解與源碼分析二(ViewGroup篇)

Android觸摸屏事件派發機制詳解與源碼分析三(Activity篇) 

AigeStudio 

Android事件分發完全解析之為什么是她 

Android事件分發完全解析之事件從何而來

 

click相關:  

Android中onTouchEvent, onClick及onLongClick的調用機制(一) 

Android中onTouchEvent, onClick及onLongClick的調用機制(二)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM