前言
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