一個最簡單的屏幕觸摸動作,理解事件分發中的3個方法

前言

android中的事件類型分爲按鍵事件和屏幕觸摸事件,Touch事件是屏幕觸摸事件的基礎事件。

一次屏幕觸摸會發生什麼?

觸發了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE…->ACTION_MOVE->ACTION_UP。到底有多少個MOVE呢?這取決於你點擊屏幕時手接觸多久屏幕。

當屏幕中包含一個ViewGroup,而這個ViewGroup又包含一個子view,這個時候android系統如何處理Touch事件呢?到底是ViewGroup來處理Touch事件,還是子view來處理Touch事件呢?onTouchEvent和OnClickListener是如何執行的?先留幾個疑問

解決疑問前,先要理清楚ViewGroup這個類中,和TouchEvent處理密切相關的3個方法:

  • public boolean dispatchTouchEvent(MotionEvent ev) 這個方法用來分發TouchEvent
  • public boolean onInterceptTouchEvent(MotionEvent ev) 這個方法用來攔截TouchEvent(View類中沒有這個方法)
  • public boolean onTouchEvent(MotionEvent ev) 這個方法用來處理TouchEvent

接下來我們來理一下邏輯
時間傳遞
一次沒有人工干預的事件是這樣的:

當TouchEvent發生時,首先交互界面(Activity或者Fragment)最頂層的ViewGroup先獲取到。TouchEvent最先到達最頂層 viewGroup的 dispatchTouchEvent 進行事件分發,此時dispatchTouchEvent 調用父類的同名方法super.dispatchTouchEvent(event)。接着到達 onInterceptTouchEvent,也是調用父類同名方法super,默認是不攔截,繼續傳遞到下一個View或者ViewGroup。如果傳到了View,先到達dispatchTouchEvent,這時候不往下傳了,因爲View的事件分發super方法和ViewGroup的不一樣,他會直接交給自己的onTouchEvent處理。

人工干預的情況,也就是改寫了上述出現的3個方法,手動返回true或者false而不是調用super方法。

總結

  • dispatchTouchEvent方法
    • View和ViewGroup不同在於當調用super時,ViewGroup會進行分發,而View會交給他的onTouchEvent處理。
    • View和ViewGroup返回true、返回false的處理邏輯都是一樣的。返回true則消費,返回false則回傳給父View的onTouchEvent處理。注意:返回false後,此時後面的事件都接收不到了,因爲會往上傳,最後由哪個View處理,以後的所有事件都交由它來處理(怎麼理解這句話呢,好比我有A、B2個ViewGroup,C是View,我重寫C裏的dispatchTouchEvent方法,讓他返回false,那麼點擊一次屏幕,AB的分發和攔截方法被調用,到C這分發返回false,直接往上傳:→B(onTouchEvent)→A(onTouchEvent)→窗口層級(onTouchEvent),此時在ABC裏MOVE和UP的事件打印見不到了,因爲後面的MOVE或者UP的事件都交給了窗口層級處理了,不信你可以在Activity層級的onTouchEvent和dispatchTouchEvent打印一下是有這2個事件的;如果你不想讓DecorView這樣的窗口層級處理,只需要在A中重寫onTouchEvent,返回true,那麼雖然BC後面的事件收不到,但是在A裏面有MOVE和UP的打印)。
  • onInterceptTouchEvent方法
    • 因爲只有ViewGroup有,所以默認的super方法和返回false都是表示不攔截,返回true則交給自己的onTouchEvent處理。
  • onTouchEvent方法
    • View和ViewGroup返回true、返回false的處理邏輯都是一樣的。不同的地方在於super方法,View是直接消費,ViewGroup則是回傳給父View的onTouchEvent處理,一直往上傳直到傳到某個ViewGroup返回true消費掉。值得注意的是,事件觸發是先觸發onTouch,再觸發onClick,如果onTouch方法返回tue,表示消費掉該事件,不在繼續進行事件傳遞,onClick也不會被調用。

弄明白大概之後,看一道小米的面試題

題目

問題解答
ACTION_DOWN在3個界面傳遞;ACTION_MOVE在A、B界面傳遞,B攔截了並調用onTouchEvent處理了。ACTION_UP在A、B界面傳遞。

問題分析
ACTION_DOWN沒有限制;ACTION_MOVE到B的時候由於onInterceptTouchEvent攔截了,直接被B的onTouchEvent處理,onInterceptTouchEvent返回true之後,看Log知道,之後onInterceptTouchEvent都不會調用了,後面的ACTION_MOVE和ACTION_UP事件都不會往下傳了,都是在B的onTouchEvent處理。具體可以看下面的日誌。

打印日誌

A

12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_DOWN
12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_DOWN
12-06 10:52:42.006 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.006 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_MOVE
12-06 10:52:42.023 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.023 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/ALayout: dispatchTouchEvent:ACTION_UP
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/ALayout: onInterceptTouchEvent:ACTION_UP

B

12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_DOWN
12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/BLayout: onInterceptTouchEvent:ACTION_DOWN
12-06 10:52:42.006 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.006 20891-20891/com.phz.testtouchevent E/BLayout: onInterceptTouchEvent:ACTION_MOVE
12-06 10:52:42.023 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.023 20891-20891/com.phz.testtouchevent E/BLayout: onTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/BLayout: onTouchEvent:ACTION_MOVE
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/BLayout: dispatchTouchEvent:ACTION_UP
12-06 10:52:42.029 20891-20891/com.phz.testtouchevent E/BLayout: onTouchEvent:ACTION_UP

C

12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/CLayout: dispatchTouchEvent:ACTION_DOWN
12-06 10:52:41.980 20891-20891/com.phz.testtouchevent E/CLayout: onTouchEvent:ACTION_DOWN
發佈了28 篇原創文章 · 獲贊 17 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章