一、簡(jiǎn)單創(chuàng)建一個(gè)Demo
-
基本結(jié)構(gòu)如下圖:

介紹:demo中將用兩個(gè)自定義View和三個(gè)自定義ViewGroup來(lái)分不同情況處理事件,下面會(huì)貼出類大致代碼:
補(bǔ)充:
View和ViewGroup的區(qū)別:
- 1.ViewGroup是View的子類,所以它也具有View的特性,但它主要用來(lái)充當(dāng)View的容器,將其中的View視作自己的孩子,對(duì)它的子View進(jìn)行管理,當(dāng)然它的孩子也可以是ViewGroup類型。
- 2.在處理事件的三個(gè)方法中,ViewGroup可以去通過(guò)onInterceptTouchEvent()方法決定是否攔截事件
1.自定義ViewGroup類

注:demo中所有的ViewGroup初始代碼基本都是這樣,只是上圖紅色塊范圍的日志打印不同而已。
2.自定義View類

注:demo中所有的View初始代碼基本都是這樣,只是上圖紅色塊范圍的日志打印不同而已。
3.布局文件:

布局簡(jiǎn)圖:

4.MainActivity基本代碼:

二、開啟大表哥:
-
先上事件分發(fā)流程圖:

-
理解事件分發(fā):
1.事件分發(fā)過(guò)程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三個(gè)方法協(xié)助完成
2.Android事件分發(fā)順序:Activity(Window) -> ViewGroup -> View
3.在事件分發(fā)三大類(Activity、ViewGroup、View)中,Activity和View不會(huì)去攔截事件(也就是不能重寫onInterceptTouchEvent()方法)
4.按照以上說(shuō)的Demo正常流程跑一遍查看一下Log日志如下(點(diǎn)擊EventViewB):

總結(jié):我們按照正常流程對(duì)事件分發(fā)加以理解,從上圖的事件流程來(lái)看,整個(gè)事件分發(fā)呈U形,從Activity開始把這個(gè)事件向下依次按照層級(jí)分發(fā)到最后的一個(gè)View或者ViewGroup,這個(gè)時(shí)候會(huì)執(zhí)行最后一個(gè)View或者ViewGroup的onTouchEvent()方法,然后又向上依次按照層級(jí)去觸發(fā)onTouchEvent()事件,中途若沒有被消費(fèi)(返回true),就會(huì)傳遞直到activity,整個(gè)流程呈U形。
-
理解dispatchTouchEvent(是否分發(fā)或傳遞事件):
先看我們demo模擬演示的日志:
模擬一:其他所有事件流程保持默認(rèn)super狀態(tài),且EventViewGroupB中的dispatchTouchEvent方法中返回false,也就是不向下分發(fā)事件,然后點(diǎn)擊EventViewB,日志如下:

模擬二:其他所有事件流程保持默認(rèn)super狀態(tài),且EventViewB中的dispatchTouchEvent方法返回false,不向下分發(fā)事件,然后點(diǎn)擊EventViewB,日志如下:

總結(jié):
1.返回false:當(dāng)我們不再向下分發(fā)的時(shí)候(dispatchTouchEvent返回false),無(wú)論是ViewGroup還是View,都會(huì)從該View的上一級(jí)的onTouchEvent事件向上傳遞(注意:當(dāng)一個(gè)View不在向下分發(fā)的時(shí)候,這個(gè)View是不會(huì)執(zhí)行自己的onTouchEvent()方法的,這也是和該View攔截后續(xù)事件的區(qū)別)
2.返回true:若直接返回true,表示事件直接被消費(fèi),這個(gè)事件也就停止分發(fā)且不會(huì)逆向向上傳遞,直接結(jié)束了。
3.返回super:事件將會(huì)繼續(xù)向下分發(fā),直到事件被消費(fèi)為止。
-
理解onInterceptTouchEvent(是否攔截該事件,默認(rèn)不做攔截):
比如我們?cè)贓ventViewGroupB中的onInterceptTouchEvent方法返回true(也就是攔截后續(xù)事件),Log日志如下:

總結(jié):注意onInterceptTouchEvent()是ViewGroup特有的方法,View和Activity都不會(huì)攔截事件。
1.返回false/super(默認(rèn)):不做此次攔截,事件將會(huì)正常向下分發(fā),分發(fā)至下級(jí)的dispatchTouchEvent方法 再次判斷是否分發(fā)事件。
2.返回true:表示ViewGroup容器攔截后續(xù)事件,會(huì)執(zhí)行該View的onTouchEvent()方法然后停止向下分發(fā)轉(zhuǎn)而通過(guò)onTouchEvent()向上傳遞,直到最終被消費(fèi)
-
理解onTouchEvent(是否消費(fèi)掉此次事件):
模擬一:我們?cè)贓ventViewB中的onTouchEvent返回true(也就是直接消費(fèi)掉此次事件),然后點(diǎn)擊EventViewB,Log日志如下:

模擬二:我們?cè)贓ventViewB中的onTouchEvent返回false(也就是不消費(fèi)掉此次事件),Log日志如下:

模擬三:我們?cè)贓ventViewB中的onTouchEvent返回super.onTouchEvent(event),Log日志和模擬二的結(jié)果一樣
總結(jié):首先,我們應(yīng)該理解onTouchEvent方法的觸發(fā)滿足的條件,在正常流程下,我們提到過(guò)整個(gè)流程呈U形,U形的轉(zhuǎn)折點(diǎn)就是從Activity開始事件向下分發(fā)到最后一個(gè)View或者ViewGroup的onTouchEvent()方法。
1.返回true:立即消費(fèi)掉事件,事件將不會(huì)向上傳遞,事件到此終止。。
2.返回false/super:不消費(fèi)掉此次事件,事件將會(huì)層層向上傳遞,直到被消費(fèi)。
-
理解事件消費(fèi):
上面很多地方說(shuō)到了事件消費(fèi),那么事件怎樣才算被消費(fèi)了呢?(答案:簡(jiǎn)而言之,就是onTouchEvent返回true就表示此次事件被消費(fèi)掉)
我們把以上demo代碼有關(guān)事件的都?xì)w到初始狀態(tài)(調(diào)用super),然后給ViewA設(shè)置觸摸事件,給ViewB設(shè)置點(diǎn)擊事件,添加以下Activity的代碼。
添加代碼1.給ViewA設(shè)置觸摸事件,默認(rèn)返回false,表示不消費(fèi)掉事件,然后向上傳遞。設(shè)置為返回true則表示消費(fèi)掉事件,終止事件傳遞。(以下是分這兩種情況 去觸摸ViewA的日志):
返回false,不消費(fèi)事件,將會(huì)向上傳遞返回true,消費(fèi)掉事件,事件終止2.點(diǎn)擊ViewB我們可以通過(guò)日志看出事件被點(diǎn)擊事件消費(fèi)掉了。
點(diǎn)擊ViewB
為了證明該事件被消費(fèi)掉了,我在ViewB 的 return super.onTouchEvent(event) 打了一個(gè)斷點(diǎn),單步調(diào)試發(fā)現(xiàn)事件最終會(huì)被消費(fèi)掉。

二、圖解總結(jié)
-
根據(jù)以上demo描述,U型圖如下:

總結(jié):
1.dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止傳遞了(到達(dá)終點(diǎn))(沒有誰(shuí)能再收到這個(gè)事件)??聪聢D中只要return true事件就沒再繼續(xù)傳下去了,對(duì)于return true我們經(jīng)常說(shuō)事件被消費(fèi)了,消費(fèi)了的意思就是事件走到這里就是終點(diǎn),不會(huì)往下傳,沒有誰(shuí)能再收到這個(gè)事件了。
2.dispatchTouchEvent 和 onTouchEvent方法在return false的時(shí)候事件都回傳給父控件的onTouchEvent處理。
- 對(duì)于dispatchTouchEvent 返回 false 的含義應(yīng)該是:事件停止往子View傳遞和分發(fā)同時(shí)開始往父控件回溯(父控件的onTouchEvent開始從下往上回傳直到某個(gè)onTouchEvent return true),事件分發(fā)機(jī)制就像遞歸,return false 的意義就是遞歸停止然后開始回溯。
- 對(duì)于onTouchEvent return false 就比較簡(jiǎn)單了,它就是不消費(fèi)事件,并讓事件繼續(xù)往父控件的方向從下往上流動(dòng)。
3.onInterceptTouchEvent 的作用
- onInterceptTouchEvent方法中 return true就會(huì)交給自己的onTouchEvent的處理,如果不攔截就是繼續(xù)往子控件往下傳。默認(rèn)是不會(huì)去攔截的,因?yàn)樽覸iew也需要這個(gè)事件,所以onInterceptTouchEvent攔截器return super.onInterceptTouchEvent()和return false是一樣的,是不會(huì)攔截的,事件會(huì)繼續(xù)往子View的dispatchTouchEvent傳遞。



