一、簡單創建一個Demo
-
基本結構如下圖:
介紹:demo中將用兩個自定義View和三個自定義ViewGroup來分不同情況處理事件,下面會貼出類大致代碼:
補充:
View和ViewGroup的區別:
- 1.ViewGroup是View的子類,所以它也具有View的特性,但它主要用來充當View的容器,將其中的View視作自己的孩子,對它的子View進行管理,當然它的孩子也可以是ViewGroup類型。
- 2.在處理事件的三個方法中,ViewGroup可以去通過onInterceptTouchEvent()方法決定是否攔截事件
1.自定義ViewGroup類
注:demo中所有的ViewGroup初始代碼基本都是這樣,只是上圖紅色塊范圍的日志打印不同而已。
2.自定義View類
注:demo中所有的View初始代碼基本都是這樣,只是上圖紅色塊范圍的日志打印不同而已。
3.布局文件:
布局簡圖:
4.MainActivity基本代碼:
二、開啟大表哥:
-
先上事件分發流程圖:
-
理解事件分發:
1.事件分發過程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三個方法協助完成
2.Android事件分發順序:Activity(Window) -> ViewGroup -> View
3.在事件分發三大類(Activity、ViewGroup、View)中,Activity和View不會去攔截事件(也就是不能重寫onInterceptTouchEvent()方法)
4.按照以上說的Demo正常流程跑一遍查看一下Log日志如下(點擊EventViewB):
總結:我們按照正常流程對事件分發加以理解,從上圖的事件流程來看,整個事件分發呈U形,從Activity開始把這個事件向下依次按照層級分發到最后的一個View或者ViewGroup,這個時候會執行最后一個View或者ViewGroup的onTouchEvent()方法,然后又向上依次按照層級去觸發onTouchEvent()事件,中途若沒有被消費(返回true),就會傳遞直到activity,整個流程呈U形。
-
理解dispatchTouchEvent(是否分發或傳遞事件):
先看我們demo模擬演示的日志:
模擬一:其他所有事件流程保持默認super狀態,且EventViewGroupB中的dispatchTouchEvent方法中返回false,也就是不向下分發事件,然后點擊EventViewB,日志如下:
模擬二:其他所有事件流程保持默認super狀態,且EventViewB中的dispatchTouchEvent方法返回false,不向下分發事件,然后點擊EventViewB,日志如下:
總結:
1.返回false:當我們不再向下分發的時候(dispatchTouchEvent返回false),無論是ViewGroup還是View,都會從該View的上一級的onTouchEvent事件向上傳遞(注意:當一個View不在向下分發的時候,這個View是不會執行自己的onTouchEvent()方法的,這也是和該View攔截后續事件的區別)
2.返回true:若直接返回true,表示事件直接被消費,這個事件也就停止分發且不會逆向向上傳遞,直接結束了。
3.返回super:事件將會繼續向下分發,直到事件被消費為止。
-
理解onInterceptTouchEvent(是否攔截該事件,默認不做攔截):
比如我們在EventViewGroupB中的onInterceptTouchEvent方法返回true(也就是攔截后續事件),Log日志如下:
總結:注意onInterceptTouchEvent()是ViewGroup特有的方法,View和Activity都不會攔截事件。
1.返回false/super(默認):不做此次攔截,事件將會正常向下分發,分發至下級的dispatchTouchEvent方法 再次判斷是否分發事件。
2.返回true:表示ViewGroup容器攔截后續事件,會執行該View的onTouchEvent()方法然后停止向下分發轉而通過onTouchEvent()向上傳遞,直到最終被消費
-
理解onTouchEvent(是否消費掉此次事件):
模擬一:我們在EventViewB中的onTouchEvent返回true(也就是直接消費掉此次事件),然后點擊EventViewB,Log日志如下:
模擬二:我們在EventViewB中的onTouchEvent返回false(也就是不消費掉此次事件),Log日志如下:
模擬三:我們在EventViewB中的onTouchEvent返回super.onTouchEvent(event),Log日志和模擬二的結果一樣
總結:首先,我們應該理解onTouchEvent方法的觸發滿足的條件,在正常流程下,我們提到過整個流程呈U形,U形的轉折點就是從Activity開始事件向下分發到最后一個View或者ViewGroup的onTouchEvent()方法。
1.返回true:立即消費掉事件,事件將不會向上傳遞,事件到此終止。。
2.返回false/super:不消費掉此次事件,事件將會層層向上傳遞,直到被消費。
-
理解事件消費:
上面很多地方說到了事件消費,那么事件怎樣才算被消費了呢?(答案:簡而言之,就是onTouchEvent返回true就表示此次事件被消費掉)
我們把以上demo代碼有關事件的都歸到初始狀態(調用super),然后給ViewA設置觸摸事件,給ViewB設置點擊事件,添加以下Activity的代碼。
添加代碼1.給ViewA設置觸摸事件,默認返回false,表示不消費掉事件,然后向上傳遞。設置為返回true則表示消費掉事件,終止事件傳遞。(以下是分這兩種情況 去觸摸ViewA的日志):
返回false,不消費事件,將會向上傳遞返回true,消費掉事件,事件終止2.點擊ViewB我們可以通過日志看出事件被點擊事件消費掉了。
點擊ViewB
為了證明該事件被消費掉了,我在ViewB 的 return super.onTouchEvent(event) 打了一個斷點,單步調試發現事件最終會被消費掉。
二、圖解總結
-
根據以上demo描述,U型圖如下:
總結:
1.dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止傳遞了(到達終點)(沒有誰能再收到這個事件)。看下圖中只要return true事件就沒再繼續傳下去了,對于return true我們經常說事件被消費了,消費了的意思就是事件走到這里就是終點,不會往下傳,沒有誰能再收到這個事件了。
2.dispatchTouchEvent 和 onTouchEvent方法在return false的時候事件都回傳給父控件的onTouchEvent處理。
- 對于dispatchTouchEvent 返回 false 的含義應該是:事件停止往子View傳遞和分發同時開始往父控件回溯(父控件的onTouchEvent開始從下往上回傳直到某個onTouchEvent return true),事件分發機制就像遞歸,return false 的意義就是遞歸停止然后開始回溯。
- 對于onTouchEvent return false 就比較簡單了,它就是不消費事件,并讓事件繼續往父控件的方向從下往上流動。
3.onInterceptTouchEvent 的作用
- onInterceptTouchEvent方法中 return true就會交給自己的onTouchEvent的處理,如果不攔截就是繼續往子控件往下傳。默認是不會去攔截的,因為子View也需要這個事件,所以onInterceptTouchEvent攔截器return super.onInterceptTouchEvent()和return false是一樣的,是不會攔截的,事件會繼續往子View的dispatchTouchEvent傳遞。