Android事件分發機制探究
瞭解Android中的事件分發機制是Android開發人員進階的必要知識,網上其實也有很多的文章介紹,但要想好好理解它,除了要有一定的耐心外還要去親自實踐。同時這個事件分發機制的博文也不好寫,要在短短的文字介紹中讓讀者理解,確實不容易,但為了加深理解於是就寫了這篇文章。
首先我們介紹一下點選事件的分發,說白了就是對MotionEvent事件的分發過程。當MotionEvent產生後,系統需要把這個事件傳遞給一個具體的view,而這個過程就是所謂的分發過程。
在事件分發中,主角有兩個,一個是ViewGroup,另一個則是View。
在ViewGroup中主要涉及三個函式:
dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()
而在View中則主要涉及兩個函式:
dispatchTouchEvent()、onTouchEvent()
我們先來熟悉一下這三個函式的功能:
dispatchTouchEvent()是來進行事件的分發。如果事件能夠傳遞給當前View,那麼此方法一定會被呼叫。返回不同結果會有不同影響,後面再進行分析。
onInterceptTouchEvent()只有在ViewGroup中才有,用來判斷是否要攔截某個事件。如果攔截,那麼在同一個事件序列當中,此方法不會被再次呼叫(即下次預設直接呼叫dispatchTouchEvent()來進行事件的分發,不會再詢問是否還要攔截事件),返回結果表示是否要攔截當前事件。
onTouchEvent()是在dispatchEvent()方法中呼叫的,表示用來處理點選事件,返回結果表示是消耗當前事件。如果不消耗,則在同一個事件序列中,當前的View無法再次接收到事件。
這裡補充一下:同一事件序列是指在手指觸控手機螢幕到離開過程,產生的一系列的DOWN、MOVE、UP事件,這個序列事件以DOWN開始,中間包含不定的MOVE事件,最後以UP結束。
首先我們知道當一個事件產生後,它是根據Activity->Window->View的順序來傳遞的。即事件總是先傳給Activity,再由Activity傳給Window,最後由Window傳遞給頂級的View。
Window會將事件傳遞給Decor View,而這個Decor View一般就是當前介面的底層容器。
首先當頂級的View獲得事件後,這裡的事件均是指ACTION_DWON。ACTION_MOVE,ACTION_UP與ACTION_DWON不同,後面會再進行分析。其實這裡的ACTION_DWON就是確定在哪一個控制元件中消耗剩下的一系列事件用的,以便後面把事件傳遞過去。
現在我們先建立自定義的檢視佈局如下:
然後在各個方法裡列印日誌。有了上面方法的分析後,再通過日誌我們便可以知道事件分發的大致流程。
比如我們點選Button1,則最先呼叫Activity的dispatchTouchEvent(),然後通過ViewGroup1的dispatchTouchEvent(),如果ViewGroup1不攔截事件,即onInterceptTouchEvent()返回為false,(onInterceptTouchEvent()預設返回的也是false),就會呼叫
ViewGroup2的dispatchTouchEvent(),同理如果ViewGroup2也不攔截,就會呼叫Button1的dispatchTouchEvent(),然後把事件傳遞給onTouchEvent()去執行。
這個就是預設情況下事件分發的流向。
接下來我們來分析那三個重要函式的返回值,產生的影響。
在ViewGroup中的dispatchTouchEvent()方法裡:
如果返回的是super.dispatchTouchEvent(event),則會對事件進行向下分發,比如上面的ViewGroup1傳遞給ViewGroup2一樣;
如果返回的是true——表示ViewGroup的dispatTouchEvent()方法自己消費掉該事件,它也不會把事件傳遞給子檢視(如ViewGroup2),也不會把事件傳遞給onTouchEvent()。並且後續的事件也只會傳遞給dispatchTouchEvent(),即事件在這裡就終止了,不會向下分發;
如果返回的是false——表示事件會傳遞給它的上一級的onTouchEvent()方法,並且後續事件不會再傳到該方法中,而是直接傳遞給它上一級的onTouchEvent()方法中。比如在ViewGroup2的dispatchTouchEvent()方法返回false,則就會呼叫ViewGroup1的onTouchEvent()方法,而在ViewGroup1返回false,就會呼叫Activity的onTouchEvent()方法。
而在View的dispatchTouchEvent()中基本一致,只是當返回super.dispatchTouchEvent(event)時,是直接傳遞給自己的onTouchEvent()方法,而不是傳遞給子檢視(View也沒有子檢視)。
在ViewGroup中的onInterceptTouchEvent()方法裡:
在前面dispatchTouchEvent()方法中,無論我們返回什麼,ViewGroup的onTouchEvent()都沒有執行。那是因為dispatchTouchEvent()要通過onInterceptTouchEvent()來實現事件傳遞給onTouchEvent()。
如果返回的是true——表示ViewGroup要對事件進行攔截,此時dispatchTouchEvent()方法便會把事件傳遞給ViewGroup的onTouchEvent(),並且下次不會再呼叫onInterceptTouchEvent()方法了,後續事件dispatchTouchEvent()會預設傳給onTouchEvent()。
如果返回的是false——表示ViewGroup不會對事件進行攔截。預設返回的就是false,此時就可以向下進行事件分發。
在onTouchEvent()方法裡:
如果返回的是true——表示控制元件(View或ViewGroup)要消耗該事件。事件傳遞到此為止,後續的事件也會陸續傳遞過來。
如果返回的是false——表示不消耗該事件。此時就會傳遞給上一級檢視的onTouchEvent()。比如在Button1的onTouchEvent()返回false,預設就會呼叫ViewGroup2的onTouchEvent()方法。如果如果所有元素都沒處理該事件,就會返回到Activity的onTouchEvent()方法中。
注意:View的onTouchEvent()預設都會消耗事件(返回true,即super.onTuchEvent()==true),除非它是不可點選的(clickable和longClickable同時為false)。View的longClickable屬性預設為false,click屬性則分情況,Button為true,TextView為false。
通過上面的講解,相信對事件分發有了一定的認識了。其實整個事件分發先從上到下,再從下到上執行。完成一個U型的流程。如下圖:
以上便是針對ACTON_DOWN的事件分發流程。
關於ACTION_MOVE與ACTION_UP的事件分發
ACTION_DOWN與ACTION_MOVE、ACTION_UP的分發是不一樣的,在上面的講解中我們針對的ACTION_DOWN的情況。ACTION_DOWN就像先行者,找到我們要消費的具體控制元件,後面才把一系列的事件傳遞過去。
因此只要收到down事件的控制元件,才會獲取到後面的move、up事件。簡單的說,就是當dispatchTouchEvent()在進行事件分發的時候,只有前一個事件(如ACTION_DOWN)返回true,才會收到ACTION_MOVE和ACTION_UP的事件。
接下來我們來驗證一下:
黑色箭頭表示:down事件傳遞方向
紅色箭頭表示:move、up事件傳遞方向
1、在ViewGroup1的dispatchTouchEvent()方法返回true,根據列印的日誌我們可以畫出down、move、up事件的流向。如下圖所示:
2、在ViewGroup2的dispatchTouchEvent()方法返回true。如下圖所示:
3、在View的dispatchTouchEvent()方法返回true。(這裡我們選擇Button1中的方法返回)。如下圖所示:
通過上面的分析我們可以得出結論:
如果在某個控制元件的dispatchEvent中返回true消費終結事件,那麼收到ACTION_DWON的函式也能收到ACTION_MOVE、ACTION_UP。
4、在ViewGroup1的onTouchEvent()方法中返回true,在Button1的onTouchEvent()返回false。我們觀察結果圖如下:
可以看到move、up事件是直接傳遞到down事件到達的控制元件的onTouchEvent()方法中的。
比如我們在ViewGroup2的onTouchEvent()方法中返回true,同樣的在Button1的onTouchEvent()返回false。結果如下圖:
我們可以得到結論:
ACTION_DOWN事件在哪個控制元件消費了(return true), 那麼ACTION_MOVE和ACTION_UP就會從上往下(通過dispatchTouchEvent())做事件分發往下傳,就只會傳到這個控制元件,不會繼續往下傳。就像前面提到的一樣,down事件像一個先行的開路者,到達後再把目的地告訴後面的事件。
5、在ViewGroup1的onTouchEvent()方法中返回true,同時在Button1中的dispatchTouchEvent()中返回false。結果如下圖:
跟前面提到的分析結果一致,同時我們可以看到在dispatchTouchEvent()返回false,會呼叫它上一級的onTouchEvent()方法,從而跳過自己的方法。
6、在ViewGroup1的onTouchEvent()方法中返回true,同時在ViewGroup2的onInterceptTouchEvent()方法中返回ture。圖如下所示:
講了這麼多,最後也畫了很多的圖,我想大致的Anroid事件的大致流程應該講解清楚了。歡迎,留言!
相關文章
- Android事件分發機制Android事件
- Android 事件分發機制的理解Android事件
- android事件分發機制詳解Android事件
- Android的MotionEvent事件分發機制Android事件
- Android事件分發機制三:事件分發工作流程Android事件
- Android View 的事件體系 -- 事件分發機制AndroidView事件
- Android事件分發機制簡單理解Android事件
- 淺談Android 事件分發機制(二)Android事件
- Android事件分發:從原始碼角度分析View事件分發機制Android事件原始碼View
- 淺談Android中的事件分發機制Android事件
- 【Android基礎】講講Android的事件分發機制Android事件
- Android事件分發機制,你瞭解過嗎?Android事件
- Android 事件分發機制原始碼解析-view層Android事件原始碼View
- 面試:講講 Android 的事件分發機制面試Android事件
- Android從原始碼角度剖析View事件分發機制Android原始碼View事件
- Android 事件分發機制原始碼詳解-最新 APIAndroid事件原始碼API
- 基於原始碼分析 Android View 事件分發機制原始碼AndroidView事件
- Android自定義View之事件分發機制總結AndroidView事件
- View事件分發機制分析View事件
- cocos EventDispatcher事件分發機制事件
- Android事件分發機制:基礎篇:最全面、最易懂Android事件
- Android事件分發機制五:面試官你坐啊Android事件面試
- android觸控事件分發機制,曾困惑你我的地方Android事件
- 事件分發機制(一):解惑篇事件
- 【朝花夕拾】Android自定義View篇之(五)Android事件分發及傳遞機制AndroidView事件
- android 事件分發Android事件
- 事件分發機制(二):原始碼篇事件原始碼
- vscode原始碼分析【五】事件分發機制VSCode原始碼事件
- 2018.03.08、View的事件分發機制筆記View事件筆記
- 拇指記者深入Android公司,打探事件分發機制背後的祕密Android事件
- 深入淺出Android事件分發機制:最全面最易懂:基礎篇(一)Android事件
- 那些你曾不知道的觸控事件—Android分發機制完全解析事件Android
- Android事件分發機制本質是樹的深度遍歷(圖+原始碼)Android事件原始碼
- 觸控事件分發核心機制優化吸收事件優化
- Android10_原理機制系列_事件傳遞機制Android事件
- Android高階進階之路【二】十分鐘徹底弄明白 View 事件分發機制AndroidView事件
- Android觸控事件傳遞機制Android事件
- 【朝花夕拾】Android自定義View篇之(七)Android事件分發機制(下)解決滑動衝突AndroidView事件
- Android面試必問!View 事件分發機制,看這一篇就夠了!Android面試View事件