Android事件分發(fā)機(jī)制,大表哥帶你慢慢深入

一、簡(jiǎn)單創(chuàng)建一個(gè)Demo

  • 基本結(jié)構(gòu)如下圖:
demo項(xiàng)目結(jié)構(gòu).png

介紹: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類

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

2.自定義View類

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

3.布局文件:

布局.png

布局簡(jiǎn)圖:

布局簡(jiǎn)圖

4.MainActivity基本代碼:

activity代碼

二、開啟大表哥:

  • 先上事件分發(fā)流程圖:
事件分發(fā)流程圖.png
  • 理解事件分發(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):

事件分發(fā)流程
總結(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,日志如下:

禁止GroupB的事件分發(fā)

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

禁止ViewB的事件分發(fā)
總結(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日志如下:

在GroupViewB中攔截后續(xù)事件
總結(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日志如下:

onTouchEvent返回true

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


onTouchEvent返回false

模擬三:我們?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)掉。

證明點(diǎn)擊事件是會(huì)消費(fèi)事件的

二、圖解總結(jié)

  • 根據(jù)以上demo描述,U型圖如下:
事件分發(fā)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傳遞。
作為筆記,事件分發(fā)就暫時(shí)先寫到這,以上若有什么紕漏煩請(qǐng)指出,多多指教。

請(qǐng)點(diǎn)贊,因?yàn)槟墓膭?lì)將是我寫作的最大動(dòng)力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容