圖解 Android 事件分發(fā)機(jī)制

在Android開發(fā)中,事件分發(fā)機(jī)制是一塊Android比較重要的知識體系,了解并熟悉整套的分發(fā)機(jī)制有助于更好的分析各種點(diǎn)擊滑動失效問題梨熙,更好去擴(kuò)展控件的事件功能和開發(fā)自定義控件,同時(shí)事件分發(fā)機(jī)制也是Android面試必問考點(diǎn)之一刀诬,如果你能把下面的一些事件分發(fā)圖當(dāng)場畫出來肯定加分不少咽扇。廢話不多說,總結(jié)一句:事件分發(fā)機(jī)制很重要陕壹。

Android 事件分發(fā)流

關(guān)于Android 事件分發(fā)機(jī)制網(wǎng)上的博文很多质欲,但是很多都是寫個(gè)Demo然后貼一下輸出的Log或者拿源碼分析,然后一堆的注釋和說明帐要,如果用心的去看肯定是收獲不少但是確實(shí)很難把整個(gè)流程說清和記住把敞。曾經(jīng)也是拼命想記住整個(gè)流程,但是一段時(shí)間又忘了榨惠,最后覺得分析這種問題和事件流的走向奋早,一張圖來解釋和說明會清晰很多盛霎,下面我根據(jù)畫的一張事件分發(fā)流程圖,說明的事件從用戶點(diǎn)擊之后,在不同函數(shù)不同返回值的情況的最終走向耽装。

圖?1

注:仔細(xì)看的話愤炸,圖分為3層,從上往下依次是Activity掉奄、ViewGroup规个、View

事件從左上角那個(gè)白色箭頭開始,由Activity的dispatchTouchEvent做分發(fā)

箭頭的上面字代表方法返回值姓建,(return true诞仓、return false、return super.xxxxx(),super 的意思是調(diào)用父類實(shí)現(xiàn)速兔。

dispatchTouchEvent和 onTouchEvent的框里有個(gè)【true---->消費(fèi)】的字墅拭,表示的意思是如果方法返回true,那么代表事件就此消費(fèi)涣狗,不會繼續(xù)往別的地方傳了谍婉,事件終止。

目前所有的圖的事件是針對ACTION_DOWN的镀钓,對于ACTION_MOVE和ACTION_UP我們最后做分析穗熬。

之前圖中的Activity 的dispatchTouchEvent 有誤(圖已修復(fù)),只有return super.dispatchTouchEvent(ev) 才是往下走丁溅,返回true 或者 false 事件就被消費(fèi)了(終止傳遞)唤蔗。

仔細(xì)看整個(gè)圖,我們得出事件流 走向的幾個(gè)結(jié)論(希望讀者專心的看下圖 1唧瘾,多看幾遍措译,腦子有比較清晰的概念。)

1饰序、如果事件不被中斷领虹,整個(gè)事件流向是一個(gè)類U型圖,我們來看下這張圖求豫,可能更能理解U型圖的意思塌衰。

圖 2

所以如果我們沒有對控件里面的方法進(jìn)行重寫或更改返回值,而直接用super調(diào)用父類的默認(rèn)實(shí)現(xiàn)蝠嘉,那么整個(gè)事件流向應(yīng)該是從Activity---->ViewGroup--->View 從上往下調(diào)用dispatchTouchEvent方法最疆,一直到葉子節(jié)點(diǎn)(View)的時(shí)候,再由View--->ViewGroup--->Activity從下往上調(diào)用onTouchEvent方法蚤告。

2努酸、dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止傳遞了(到達(dá)終點(diǎn))(沒有誰能再收到這個(gè)事件)《徘。看下圖中只要return true事件就沒再繼續(xù)傳下去了获诈,對于return true我們經(jīng)常說事件被消費(fèi)了仍源,消費(fèi)了的意思就是事件走到這里就是終點(diǎn),不會往下傳舔涎,沒有誰能再收到這個(gè)事件了笼踩。

圖 3.

3、dispatchTouchEvent 和 onTouchEvent return false的時(shí)候事件都回傳給父控件的onTouchEvent處理亡嫌。

圖 4.

看上圖深藍(lán)色的線嚎于,對于返回false的情況,事件都是傳給父控件onTouchEvent處理挟冠。

對于dispatchTouchEvent 返回 false 的含義應(yīng)該是:事件停止往子View傳遞和分發(fā)同時(shí)開始往父控件回溯(父控件的onTouchEvent開始從下往上回傳直到某個(gè)onTouchEvent return true)于购,事件分發(fā)機(jī)制就像遞歸,return false 的意義就是遞歸停止然后開始回溯知染。

對于onTouchEvent return false 就比較簡單了价涝,它就是不消費(fèi)事件,并讓事件繼續(xù)往父控件的方向從下往上流動持舆。

4、dispatchTouchEvent伪窖、onTouchEvent逸寓、onInterceptTouchEvent

ViewGroup 和View的這些方法的默認(rèn)實(shí)現(xiàn)就是會讓整個(gè)事件安裝U型完整走完,所以 return super.xxxxxx() 就會讓事件依照U型的方向的完整走完整個(gè)事件流動路徑)覆山,中間不做任何改動竹伸,不回溯、不終止簇宽,每個(gè)環(huán)節(jié)都走到勋篓。


所以如果看到方法return super.xxxxx() 那么事件的下一個(gè)流向就是走U型下一個(gè)目標(biāo),稍微記住上面這張圖魏割,你就能很快判斷出下一個(gè)走向是哪個(gè)控件的哪個(gè)函數(shù)譬嚣。

5、onInterceptTouchEvent 的作用

圖 5.

Intercept 的意思就攔截钞它,每個(gè)ViewGroup每次在做分發(fā)的時(shí)候拜银,問一問攔截器要不要攔截(也就是問問自己這個(gè)事件要不要自己來處理)如果要自己處理那就在onInterceptTouchEvent方法中 return true就會交給自己的onTouchEvent的處理,如果不攔截就是繼續(xù)往子控件往下傳遭垛。默認(rèn)是不會去攔截的尼桶,因?yàn)樽覸iew也需要這個(gè)事件,所以onInterceptTouchEvent攔截器return super.onInterceptTouchEvent()和return false是一樣的锯仪,是不會攔截的泵督,事件會繼續(xù)往子View的dispatchTouchEvent傳遞

6庶喜、ViewGroup 和View 的dispatchTouchEvent方法返回super.dispatchTouchEvent()的時(shí)候事件流走向小腊。

圖 6

首先看下ViewGroup 的dispatchTouchEvent救鲤,之前說的return true是終結(jié)傳遞。return false 是回溯到父View的onTouchEvent溢豆,然后ViewGroup怎樣通過dispatchTouchEvent方法能把事件分發(fā)到自己的onTouchEvent處理呢蜒简,return true和false 都不行,那么只能通過Interceptor把事件攔截下來給自己的onTouchEvent漩仙,所以ViewGroup dispatchTouchEvent方法的super默認(rèn)實(shí)現(xiàn)就是去調(diào)用onInterceptTouchEvent搓茬,記住這一點(diǎn)

那么對于View的dispatchTouchEvent return super.dispatchTouchEvent()的時(shí)候呢事件會傳到哪里呢队他,很遺憾View沒有攔截器卷仑。但是同樣的道理return true是終結(jié)。return false 是回溯會父類的onTouchEvent麸折,怎樣把事件分發(fā)給自己的onTouchEvent 處理呢锡凝,那只能return super.dispatchTouchEvent,View類的dispatchTouchEvent()方法默認(rèn)實(shí)現(xiàn)就是能幫你調(diào)用View自己的onTouchEvent方法的。

說了這么多垢啼,不知道有說清楚沒有窜锯,我這邊最后總結(jié)一下:

對于 dispatchTouchEvent,onTouchEvent芭析,return true是終結(jié)事件傳遞锚扎。return false 是回溯到父View的onTouchEvent方法。

ViewGroup 想把自己分發(fā)給自己的onTouchEvent馁启,需要攔截器onInterceptTouchEvent方法return true 把事件攔截下來驾孔。

ViewGroup 的攔截器onInterceptTouchEvent 默認(rèn)是不攔截的,所以return super.onInterceptTouchEvent()=return false惯疙;

View 沒有攔截器翠勉,為了讓View可以把事件分發(fā)給自己的onTouchEvent,View的dispatchTouchEvent默認(rèn)實(shí)現(xiàn)(super)就是把事件分發(fā)給自己的onTouchEvent霉颠。

ViewGroup和View 的dispatchTouchEvent 是做事件分發(fā)对碌,那么這個(gè)事件可能分發(fā)出去的四個(gè)目標(biāo)

注:------> 后面代表事件目標(biāo)需要怎么做。

1蒿偎、 自己消費(fèi)俭缓,終結(jié)傳遞。------->return true 酥郭;

2华坦、 給自己的onTouchEvent處理-------> 調(diào)用super.dispatchTouchEvent()系統(tǒng)默認(rèn)會去調(diào)用 onInterceptTouchEvent,在onInterceptTouchEvent return true就會去把事件分給自己的onTouchEvent處理不从。

3惜姐、 傳給子View------>調(diào)用super.dispatchTouchEvent()默認(rèn)實(shí)現(xiàn)會去調(diào)用 onInterceptTouchEvent 在onInterceptTouchEvent return false,就會把事件傳給子類。

4歹袁、 不傳給子View坷衍,事件終止往下傳遞,事件開始回溯条舔,從父View的onTouchEvent開始事件從下到上回歸執(zhí)行每個(gè)控件的onTouchEvent------->return false枫耳;

注:由于View沒有子View所以不需要onInterceptTouchEvent 來控件是否把事件傳遞給子View還是攔截,所以View的事件分發(fā)調(diào)用super.dispatchTouchEvent()的時(shí)候默認(rèn)把事件傳給自己的onTouchEvent處理(相當(dāng)于攔截)孟抗,對比ViewGroup的dispatchTouchEvent 事件分發(fā)迁杨,View的事件分發(fā)沒有上面提到的4個(gè)目標(biāo)的第3點(diǎn)。

ViewGroup和View的onTouchEvent方法是做事件處理的凄硼,那么這個(gè)事件只能有兩個(gè)處理方式:

1铅协、自己消費(fèi)掉,事件終結(jié)摊沉,不再傳給誰----->return true;

2狐史、繼續(xù)從下往上傳,不消費(fèi)事件说墨,讓父View也能收到到這個(gè)事件----->return false;View的默認(rèn)實(shí)現(xiàn)是不消費(fèi)的骏全。所以super==false。

ViewGroup的onInterceptTouchEvent方法對于事件有兩種情況:

1尼斧、攔截下來吟温,給自己的onTouchEvent處理--->return true;

2、不攔截突颊,把事件往下傳給子View---->return false,ViewGroup默認(rèn)是不攔截的,所以super==false潘悼;

關(guān)于ACTION_MOVE 和 ACTION_UP

上面講解的都是針對ACTION_DOWN的事件傳遞律秃,ACTION_MOVE和ACTION_UP在傳遞的過程中并不是和ACTION_DOWN 一樣,你在執(zhí)行ACTION_DOWN的時(shí)候返回了false治唤,后面一系列其它的action就不會再得到執(zhí)行了棒动。簡單的說,就是當(dāng)dispatchTouchEvent在進(jìn)行事件分發(fā)的時(shí)候宾添,只有前一個(gè)事件(如ACTION_DOWN)返回true船惨,才會收到ACTION_MOVE和ACTION_UP的事件。具體這句話很多博客都說了缕陕,但是具體含義是什么呢粱锐?我們來看一下下面的具體分析。

上面提到過了扛邑,事件如果不被打斷的話是會不斷往下傳到葉子層(View)怜浅,然后又不斷回傳到Activity,dispatchTouchEvent 和 onTouchEvent 可以通過return true 消費(fèi)事件,終結(jié)事件傳遞恶座,而onInterceptTouchEvent 并不能消費(fèi)事件搀暑,它相當(dāng)于是一個(gè)分叉口起到分流導(dǎo)流的作用,ACTION_MOVE和ACTION_UP 會在哪些函數(shù)被調(diào)用跨琳,之前說了并不是哪個(gè)函數(shù)收到了ACTION_DOWN自点,就會收到 ACTION_MOVE 等后續(xù)的事件的。

下面通過幾張圖看看不同場景下脉让,ACTION_MOVE事件和ACTION_UP事件的具體走向并總結(jié)一下規(guī)律桂敛。

1、我們在ViewGroup1 的dispatchTouchEvent 方法返回true消費(fèi)這次事件

ACTION_DOWN 事件從(Activity的dispatchTouchEvent)--------> (ViewGroup1 的dispatchTouchEvent) 后結(jié)束傳遞侠鳄,事件被消費(fèi)(如下圖紅色的箭頭代碼ACTION_DOWN 事件的流向)埠啃。

//打印日志Activity | dispatchTouchEvent--> ACTION_DOWNViewGroup1 | dispatchTouchEvent--> ACTION_DOWN---->消費(fèi)

在這種場景下ACTION_MOVE和ACTION_UP 將如何呢,看下面的打出來的日志

Activity | dispatchTouchEvent--> ACTION_MOVEViewGroup1 | dispatchTouchEvent--> ACTION_MOVE----TouchEventActivity | dispatchTouchEvent--> ACTION_UPViewGroup1 | dispatchTouchEvent--> ACTION_UP----

下圖中

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

2伟恶、我們在ViewGroup2 的dispatchTouchEvent 返回true消費(fèi)這次事件

Activity | dispatchTouchEvent--> ACTION_DOWNViewGroup1 | dispatchTouchEvent--> ACTION_DOWNViewGroup1 | onInterceptTouchEvent--> ACTION_DOWNViewGroup2 | dispatchTouchEvent--> ACTION_DOWN---->消費(fèi)Activity | dispatchTouchEvent--> ACTION_MOVEViewGroup1 | dispatchTouchEvent--> ACTION_MOVEViewGroup1 | onInterceptTouchEvent--> ACTION_MOVEViewGroup2 | dispatchTouchEvent--> ACTION_MOVE----TouchEventActivity | dispatchTouchEvent--> ACTION_UPViewGroup1 | dispatchTouchEvent--> ACTION_UPViewGroup1 | onInterceptTouchEvent--> ACTION_UPViewGroup2 | dispatchTouchEvent--> ACTION_UP----

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

Paste_Image.png

3碴开、我們在View 的dispatchTouchEvent 返回true消費(fèi)這次事件

這個(gè)我不就畫圖了,效果和在ViewGroup2 的dispatchTouchEvent return true的差不多博秫,同樣的收到ACTION_DOWN 的dispatchTouchEvent函數(shù)都能收到 ACTION_MOVE和ACTION_UP潦牛。

所以我們就基本可以得出結(jié)論如果在某個(gè)控件的dispatchTouchEvent 返回true消費(fèi)終結(jié)事件,那么收到ACTION_DOWN 的函數(shù)也能收到 ACTION_MOVE和ACTION_UP挡育。

4巴碗、我們在View 的onTouchEvent 返回true消費(fèi)這次事件

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

5、我們在ViewGroup 2 的onTouchEvent 返回true消費(fèi)這次事件

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

6即寒、我們在ViewGroup 1 的onTouchEvent 返回true消費(fèi)這次事件

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

7橡淆、我們在Activity 的onTouchEvent 返回true消費(fèi)這次事件

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

8、我們在View的dispatchTouchEvent 返回false并且Activity 的onTouchEvent 返回true消費(fèi)這次事件

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

9母赵、我們在View的dispatchTouchEvent 返回false并且ViewGroup 1 的onTouchEvent 返回true消費(fèi)這次事件

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

10逸爵、我們在View的dispatchTouchEvent 返回false并且在ViewGroup 2 的onTouchEvent 返回true消費(fèi)這次事件

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

11、我們在ViewGroup2的dispatchTouchEvent 返回false并且在ViewGroup1 的onTouchEvent返回true消費(fèi)這次事件

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

12凹嘲、我們在ViewGroup2的onInterceptTouchEvent 返回true攔截此次事件并且在ViewGroup 1 的onTouchEvent返回true消費(fèi)這次事件师倔。

紅色的箭頭代表ACTION_DOWN 事件的流向

藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向

一下子畫了好多圖,還有好幾種情況就不再畫了周蹭,相信你也看出規(guī)律了趋艘,對于在onTouchEvent消費(fèi)事件的情況:在哪個(gè)View的onTouchEvent 返回true,那么ACTION_MOVE和ACTION_UP的事件從上往下傳到這個(gè)View后就不再往下傳遞了凶朗,而直接傳給自己的onTouchEvent 并結(jié)束本次事件傳遞過程瓷胧。

對于ACTION_MOVE、ACTION_UP總結(jié):ACTION_DOWN事件在哪個(gè)控件消費(fèi)了(return true)棚愤, 那么ACTION_MOVE和ACTION_UP就會從上往下(通過dispatchTouchEvent)做事件分發(fā)往下傳抖单,就只會傳到這個(gè)控件,不會繼續(xù)往下傳,如果ACTION_DOWN事件是在dispatchTouchEvent消費(fèi)矛绘,那么事件到此為止停止傳遞耍休,如果ACTION_DOWN事件是在onTouchEvent消費(fèi)的,那么會把ACTION_MOVE或ACTION_UP事件傳給該控件的onTouchEvent處理并結(jié)束傳遞货矮。

tips : 最近剛做了一個(gè)自定義控件羊精,里面做了不少事件分發(fā)的處理和交互,個(gè)人是覺得可以當(dāng)做本篇文章的一個(gè)實(shí)踐囚玫,大家可以看下源碼事件分發(fā)相關(guān)的的部分代碼喧锦,可能更有體會,鏈接地址:CalendarListView

注:【轉(zhuǎn)載請注明抓督,問題可提問燃少,喜歡可打賞,博客持續(xù)更新铃在,歡迎關(guān)注】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阵具,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子定铜,更是在濱河造成了極大的恐慌阳液,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揣炕,死亡現(xiàn)場離奇詭異帘皿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)畸陡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門鹰溜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丁恭,你說我怎么就攤上這事曹动。” “怎么了涩惑?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長桑驱。 經(jīng)常有香客問我竭恬,道長,這世上最難降的妖魔是什么熬的? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任痊硕,我火速辦了婚禮,結(jié)果婚禮上押框,老公的妹妹穿的比我還像新娘岔绸。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布盒揉。 她就那樣靜靜地躺著晋被,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刚盈。 梳的紋絲不亂的頭發(fā)上羡洛,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機(jī)與錄音藕漱,去河邊找鬼欲侮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肋联,可吹牛的內(nèi)容都是我干的威蕉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼橄仍,長吁一口氣:“原來是場噩夢啊……” “哼韧涨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沙兰,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤氓奈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后鼎天,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舀奶,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年斋射,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了育勺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,146評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡罗岖,死狀恐怖涧至,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情桑包,我是刑警寧澤南蓬,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站哑了,受9級特大地震影響赘方,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弱左,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一窄陡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拆火,春花似錦跳夭、人聲如沸涂圆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽润歉。三九已至,卻和暖如春套硼,著一層夾襖步出監(jiān)牢的瞬間卡辰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工邪意, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留九妈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓雾鬼,卻偏偏與公主長得像萌朱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子策菜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容