事件分發機制(一):解惑篇

散人丶發表於2019-04-15

前言

關於事件分發的文章網上資料魚龍混雜,常常讓人看著當時覺得是那麼回事,但是事後往往很難讓人理解其根本原因,導致每次面試前夕又要把相關文章再讀幾遍,往復為之,本文粗略解析下事件分發機制,後續會分析下原始碼,希望能夠幫助到大家解惑一二。

從一個例子說起

在實踐中,大多數需要了解事件分發機制的可能就是滑動衝突了,需要了解什麼時候到底誰應該處理這個事件,先來看個例子,當我們點選區域View

事件分發機制(一):解惑篇

預設不處理事件

假如ViewGroup和View都預設不處理事件,不復寫對應函式

  • DOWN事件傳遞到View的onTouchEvent,返回false,表示View本身不關心這次事件
  • DOWN事件傳遞到ViewGroup的onTouchEvent,返回false,表示ViewGroup本身不關心這次事件
  • 最終事件回溯到Activity中,由最上層處理事件

由於onTouchEvent中返回了false,同時也就代表著不關心本次事件(我不關心這次手勢,不要來找我了),後續的MOVE,UP事件也不會傳遞進來

View處理事件

假設View是需要處理事件,比如是Button之類的View,預設是可點選的

  • DOWN事件傳遞到View的onTouchEvent,返回True,處理自己的邏輯
  • 因為View中的onTouchEvent返回了True,表示有人願意處理了這個事件,那麼後續事件將不會傳遞到ViewGroup以及上層的onTouchEvent中了,後續的事件將由View本身進行處理

注意上述的前提是ViewGroup不會對此事件進行攔截,因為在DOWN事件傳遞到View的ontouchEvent之前,ViewGroup是有一次攔截機會的,如果ViewGroup的onInterceptTouchEvent返回false,則不攔截,但是注意下一次的MOVE、UP等事件還是會走ViewGroup攔截的判斷,可根據邏輯進行攔截處理,如果沒有攔截,但是View的ontouchEvent返回了false,也就表示下面沒有View是願意處理這個事件的,那麼這個燙手的山芋(事件)還是會回到ViewGroup的ontouchEvent中

同時可見DOWN事件的返回值,其實就是表示著View本身對本次事件處理的意願如何,True則代表著願意處理該事件,false則代表不關心本次事件

ViewGroup攔截事件

假如ViewGroup在DOWN事件中,攔截了事件onInterceptTouchEvent返回了true,那麼此時事件直接轉到ViewGroup的ontouchEvent中,後續的MOVE、UP事件也會交給ViewGroup處理,View是沒有機會處理到事件的,即使此時呼叫requestDisallowInterceptTouchEvent也是無效的

假如ViewGroup在DOWN事件中沒有攔截事件,但是在MOVE中卻對事件進行了攔截處理,比如類似如ScrollView一樣,是可以對其中的View進行點選處理的,但是在滑動時,ScrollView需要處理自己的邏輯,這時候就在MOVE中攔截了事件

  • DOWN事件傳遞在ViewGroup中,但此時並不想攔截,onInterceptTouchEvent返回false,事件傳遞到View的ontouchEvent中,消費掉返回True
  • MOVE事件傳遞到ViewGroup中,此時ViewGroup需要對事件進行攔截處理,onInterceptTouchEvent返回True,但是View還苦巴巴的等著事件呢,因為之前在ontouchEvent中返回了True,那麼此時這個MOVE事件將會被系統變成一個CANCEL事件,這個CANCEL事件將會傳遞給VIEW的onTouchEvent方法,告訴你,別等了,你的事件被取消了
  • 當MOVE事件或者UP事件再次進入ViewGroup的時候,注意此時由於之前已經攔截了事件,此次事件並不會走onInterceptTouchEvent的判斷,可以理解為onInterceptTouchEvent一旦返回了True,那麼後續的一系列事件就預設已經被攔截處理了,不會再去判斷!而是會直接傳遞給ViewGroup的onTouchEvent方法去處理了
  • 此時View就不會受到任何的事件了

總結

  • 同一時間序列事件是指以down事件開始,中間含有數量不定的move事件,最終以up事件結束。

  • 各個View的onTouchEvent方法對DOWN事件的處理,代表了該View對以此DOWN開始的整個手勢事件的處理意願,True代表需要處理此次事件,false則表示不關心,事件會回傳到上層View的ontouchEvent中

  • 如果ViewGroup一旦決定攔截一個事件,也就是onInterceptTouchEvent返回True,那麼後續的同一時間序列事件將會被預設攔截,不會再呼叫onInterceptTouchEvent方法,後續事件會交給ontouchEvent進行處理

  • 事件一旦交給一個View處理,那麼它就必須消耗掉,否則同一事件序列中剩下的事件就不再交給他來處理了,如果ontouchEvent中返回false,那麼後續事件不會再傳遞進來

  • 如果ViewGroup攔截了一個半路的事件(比如,MOVE),這個事件將會被系統變成一個CANCEL事件,並傳遞給之前處理該手勢(gesture)的子View,而且不會再傳遞(無論是被攔截的MOVE還是系統生成的CANCEL)給ViewGroup的onTouchEvent方法。只有再到來的事件才會傳遞到ViewGroup的onTouchEvent方法中。

請幫頂 / 評論點贊!因為你的鼓勵是我寫作的最大動力!

相關文章