Android事件分發機制,大表哥帶你慢慢深入

一、簡單創建一個Demo

  • 基本結構如下圖:
demo項目結構.png

介紹:demo中將用兩個自定義View和三個自定義ViewGroup來分不同情況處理事件,下面會貼出類大致代碼:
補充:
View和ViewGroup的區別:

  • 1.ViewGroup是View的子類,所以它也具有View的特性,但它主要用來充當View的容器,將其中的View視作自己的孩子,對它的子View進行管理,當然它的孩子也可以是ViewGroup類型。
  • 2.在處理事件的三個方法中,ViewGroup可以去通過onInterceptTouchEvent()方法決定是否攔截事件

1.自定義ViewGroup類

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

2.自定義View類

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

3.布局文件:

布局.png

布局簡圖:

布局簡圖

4.MainActivity基本代碼:

activity代碼

二、開啟大表哥:

  • 先上事件分發流程圖:
事件分發流程圖.png
  • 理解事件分發:

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,日志如下:

禁止GroupB的事件分發

模擬二:其他所有事件流程保持默認super狀態,且EventViewB中的dispatchTouchEvent方法返回false,不向下分發事件,然后點擊EventViewB,日志如下:

禁止ViewB的事件分發
總結:

1.返回false:當我們不再向下分發的時候(dispatchTouchEvent返回false),無論是ViewGroup還是View,都會從該View的上一級的onTouchEvent事件向上傳遞(注意:當一個View不在向下分發的時候,這個View是不會執行自己的onTouchEvent()方法的,這也是和該View攔截后續事件的區別
2.返回true:若直接返回true,表示事件直接被消費,這個事件也就停止分發且不會逆向向上傳遞,直接結束了。
3.返回super:事件將會繼續向下分發,直到事件被消費為止。

  • 理解onInterceptTouchEvent(是否攔截該事件,默認不做攔截):

比如我們在EventViewGroupB中的onInterceptTouchEvent方法返回true(也就是攔截后續事件),Log日志如下:

在GroupViewB中攔截后續事件
總結:注意onInterceptTouchEvent()是ViewGroup特有的方法,View和Activity都不會攔截事件。

1.返回false/super(默認):不做此次攔截,事件將會正常向下分發,分發至下級的dispatchTouchEvent方法 再次判斷是否分發事件。
2.返回true:表示ViewGroup容器攔截后續事件,會執行該View的onTouchEvent()方法然后停止向下分發轉而通過onTouchEvent()向上傳遞,直到最終被消費

  • 理解onTouchEvent(是否消費掉此次事件):

模擬一:我們在EventViewB中的onTouchEvent返回true(也就是直接消費掉此次事件),然后點擊EventViewB,Log日志如下:

onTouchEvent返回true

模擬二:我們在EventViewB中的onTouchEvent返回false(也就是不消費掉此次事件),Log日志如下:


onTouchEvent返回false

模擬三:我們在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型圖如下:
事件分發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傳遞。
作為筆記,事件分發就暫時先寫到這,以上若有什么紕漏煩請指出,多多指教。

請點贊,因為您的鼓勵將是我寫作的最大動力!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容