Android事件分發機制解析

今天結合流程圖和代碼來對Android事件分發機製做一個總結,我自己起一個叫法就是「3個3」。

跟事件分發相關的主要有三個節點方法:

1.dispatchTouchEvent

2.onInterceptTouchEvent(這個只有ViewGroup有)

3.onTouchEvent

為了簡單扼要,Demo總共就三個研究對象,Activity/ViewGroup/View,從屬關係就是Activity中載入ViewGroup, ViewGroup中有一個View是Button,之所以用Button就是為了點擊事件。在Button點擊的時候看下三者事件分發的順序。

在Activity中, 主要就是添加幾個Log,在`onTouchEvent`中列印出MotionEvent事件,這裡為了簡單主要關注

1.ACTION_DOWN 按下事件

2.ACTION_MOVE 移動事件

3.ACTION_UP 鬆開事件

@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.w(Constants.TAG, "------------------------------------------"); Log.d(Constants.TAG, "MainActivity.dispatchTouchEvent"); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { String eventString; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: eventString = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: eventString = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: eventString = "ACTION_UP"; break; default: eventString = "OTHER_EVENT"; break; } Log.d(Constants.TAG, "MainActivity.onTouchEvent: " + eventString); return super.onTouchEvent(event); }

在ViewGroup中,多了一個onInterceptTouchEvent方法。

@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.d(Constants.TAG, "ViewGroupCustom.dispatchTouchEvent"); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.d(Constants.TAG, "ViewGroupCustom.onInterceptTouchEvent"); return true; } @Override public boolean onTouchEvent(MotionEvent event) { String eventString; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: eventString = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: eventString = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: eventString = "ACTION_UP"; break; default: eventString = "OTHER_EVENT"; break; } Log.d(Constants.TAG, "ViewGroupCustom.onTouchEvent: " + eventString); return true; }

在View中方法和Activity中一樣:

@Override public boolean dispatchTouchEvent(MotionEvent event) { Log.d(Constants.TAG, "ViewCustom.dispatchTouchEvent"); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { String eventString; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: eventString = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: eventString = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: eventString = "ACTION_UP"; break; default: eventString = "OTHER_EVENT"; break; } Log.d(Constants.TAG, "ViewCustom.onTouchEvent: " + eventString); return super.onTouchEvent(event); }

默認三個方法都是調用super的方法。

稍微總結下,涉及到**三個3**

1.第一個**3**就是三個研究對象:Activity/ViewGroup/View

2.第二個**3**就是三個方法:dispatchTouchEvent/onInterceptTouchEvent/onTouchEvent

3.第二個**3**就是每個方法都return3中狀態之一:super/true/false

接下來就是排列組合,事件分發就是在三個對象,三個方法,三個狀態之間進行各種排列組合,就組合成事件分發的多種狀態。接下來上一個很重要的圖,就是整體的事件分發流程圖:

EventDispatch

上面這張流程圖很重要,今天這個解析就是圍繞著它轉了。下面就是分別把上面不同狀態進行詳細解釋。

先做個說明,在按鈕點擊的時候,我們就關注三個事件,ACTION_DOWN/ACTION_MOVE/ACTION_UP。

首先是三個對象的三個方法都不做更改,也就是都調用super,這就是默認狀態。這樣三個事件都會到按鈕的onTouchEvent,並且調用onClick回調方法。

Default

接下來開始做點手腳,在Activity的dispatchTouchEvent中返回true狀態,這樣Activity就消費掉事件,不再往下傳給另外兩個對象,甚至也不調用自己的onTouchEvent方法。

activity_dispatch_true

Activity的dispatchTouchEvent中返回false,也是自己消費點,和上面返回true是一樣的。

activity_dispatch_false

接下來activity兩個方法保持默認,也就是super狀態,這樣事件就能傳遞到viewgroup了。然後對viewgroup動手腳,viewgroup有三個方法,所以狀態會多一點。首先在第一個方法dispatchTouchEvent返回true,這樣和acticity一樣的,自己直接消費掉,也不給自己的onTouchEvent。

viewgroup_dispatch_true

如果把狀態改成false呢?這種情況也就類似於員工反了不幹了,只能領導自己干,事件就會給activity的onTouchEvent消費,再之後的move和up事件不會再分發了,activity直接給自己的onTouchEvent消費。

viewgroup_dispatch_false

接下來就是viewgroup的第二個方法onInterceptTouchEvent了。如果第一個方法默認返回super狀態,那麼就會把事件給這個方法,viewgroup通過這個方法來告訴系統攔不攔截這個時間。返回true就是攔截,事件就會給自己的第三個方法onTouchEvent消費。如果onTouchEvent返回super或者false,那麼事件就會給父類activity消費。之後事件不再傳給viewgroup,activity自己直接消費。這就類似於老闆交給員工任務,員工沒完成好,老闆以後就不交給這個員工了。

viewgroup_onIntercept_true

如果viewgroup的第二個方法返回false,表示自己不做攔截,那麼事件就會傳遞給子類,這裡就是button了。button就默認給自己的onTouchEvent消費掉。

viewgroup_onIntercept_false

如果第二個方法返回true表示攔截,事件就會給自己的onTouchEvent消費,onTouchEvent返回true,事件就是viewgroup自己消費,後續的事件也會給到viewgroup。

viewgroup_onIntercept_true_onTouch_true

如果onTouchEvent返回false,事件就會給父類activity消費。之後事件不再傳給viewgroup,activity自己直接消費。

viewgroup_onIntercept_true_onTouch_false

最後就是最後一個對象view,在這裡是button。view只有兩個方法,沒有onInterceptTouchEvent。首先如果dispatchTouchEvent返回true,那麼事件就直接消費掉了,不傳遞給自己的onTouchEvent方法。

view_dispatch_true

dispatchTouchEvent返回false就會把事件給父類的onTouchEvent消費。以後事件不再交給這個view。

view_dispatch_false

view的onTouchEvent方法如果返回true,那麼事件就會自己消費點,並且不會調用onClick這個回調方法。

view_onTouch_true

如果onTouchEvent返回false,那麼事件就會交給父類,這個系列剩下的事件就不會再交給這個view了。

view_onTouch_false

到這裡事件分發就說的差不多了,我們這個Demo比較簡單,但是不影響理解原理。簡單坐下總結:

> 1.對於dispatchTouchEvent這個方法,返回true都是直接消費掉,不做其他傳遞。返回false就有點區別,對於activity是和true一樣直接消費掉,對於viewgroup和view就是把事件給父類的onTouchEvent消費。返回super就都是進行分發

2.onInterceptTouchEvent這個方法只有viewgroup有,返回true就是攔截,會把事件給到自己的onTouchEvent消費;返回false和返回super是一樣的,不攔截,分發給子view

3.onTouchEvent返回true就是消費掉事件了,如果返回false就傳遞給父類。返回super有點區別,對於viewgroup就和false一樣,傳遞給父類;對於view就會再接著往下傳遞,比如調用點擊回調等。

到這裡就把事件分發說的差不多了,沒有放上源碼分析,我是覺得那樣內容就有點多,容易亂,如果面試的時候畫出上面的流程圖就差不多了。

如果本文對你有幫助,請點個贊哈,謝謝!


推薦閱讀:

工商銀行面試真題彙編和精彩回答
奇葩而有效的反向面試——王猛見桓溫
光大銀行面試問題精彩回答(50問真題彙編)
四項基本原則,讓面試官為你的機智點贊!
日本面試注意事項詳解【問答篇】

TAG:Android | 面試 | Android開發 |