轉載請以鏈接形式標明出處:
本文出自:103style的博客
《Android開發藝術探索》 學習記錄
base on Android-29
驗證和分析Android的事件分發機制 -- 驗證部分的記錄
目錄
- 一些結論
- 實例驗證
- 驗證結果的小結
一些結論
這裏我們先列出一些 《Android開發藝術探索》 中的結論,然後下一步我們來驗證它們:
1、同一個事件序列是指從手指接觸屏幕那一刻起,到離開屏幕結束,是以 down 事件開始, 中間有數量不定的 move 事件,最終以 up 事件結束。
2、正常情況下,一個事件序列只能被一個 View 攔截且消耗。這條原因可參考第三點。
3、某個 View 一旦決定攔截,那麼這個事件序列都只能由他來處理(前提是事件能傳到它),並且它的
onInterceptTouchEvent
不會在被調用。這點也很好理解,就是說一個 View 決定攔截一個事件後,那麼系統會把這個事件序列內的其他事件都交給他來處理,因此就不會再去調用onInterceptTouchEvent
來詢問是否攔截了。4、某個View 一旦開始處理事件,如果他不消耗 ACTION_DOWN 事件,那麼同一序列的其他事件都不會交給它處理,並且事件將重新交給它的父元素去處理,即父元素的
onTouchEvent
會被調用。就像一件重要的事你沒處理好,短期內上級就不敢再把重要的事情交給你做了。5、如果 View 不消耗除 ACTION_DOWN 以外的事件,那麼這個點擊事件就會消失,此時父元素的
onTouchEvent
不會被調用,並且當前 View 能收到後續的事件,最終這些事件都交給 Activity 處理。6、ViewGroup 默認不攔截任何事件。ViewGroup 源碼中
onInterceptTouchEvent
默認返回false
。7、View 沒有
onInterceptTouchEvent
方法,一旦事件傳給他,它的onTouchEvent
就會被調用。8、View 的
onTouchEvent
默認都會消耗事件(返回true
),除非它是不可點擊的(clickable 和 longClickable 都爲false
)。View 的 longClickable 屬性默認爲false
;clickable 分情況,比如 Button 默認爲true
,TextView 默認爲false
。9、View 的 enable 屬性不影響
onTouchEvent
的默認返回值,哪怕 View 是 disable 狀態的,只要clickable 和 longClickable 其中一個爲true
, 那麼onTouchEvent
就返回true
.10、
onClick
會觸發的前提是當前 View 是可以點擊的,並且收到了 down 和 up 事件。11、事件的傳遞過程是 由外向內 的,即事件總是先傳遞給父元素,然後再由父元素分發給子 View,通過
requestDisallowInterceptTouchEvent(boolean)
方法可以干預父元素的分發過程,但是 ACTION_DOWN 除外。
示例驗證
這裏大家主要關注打印的日誌信息,看下事件分發的過程即可。
我們分別創建監聽上面三個方法的 ViewGroup 和 View,然後在測試 Activity 中重寫上面的方法,源碼鏈接如下:
TestLinearLayout.java、TestView.java、EventHandler.java、MainActivity.java、activity_main.xml
reset 點擊之後的處理邏輯。
TestLinearLayout.setIsIntercept(false);
TestLinearLayout.setInterceptEvent(-1024);
testLinearLayout.setOnClickListener(null);
testView.setOnClickListener(null);
testLinearLayout.setClickable(false);
testView.setClickable(false);
我們通過以下測試方法來驗證:
除了默認,其他沒有設置點擊事件時,都設置爲不點擊,即setClickable(fasle)
- 默認什麼都不做
- View 設置點擊事件
- ViewGroup 設置點擊事件
- View 和 ViewGroup 都設置點擊事件
- ViewGroup 僅攔截全部事件,即
onInterceptTouchEvent
返回true
- ViewGroup 攔截全部事件,並設置 ViewGroup 的點擊事件
- ViewGroup 攔截全部事件,並設置 View 的點擊事件
- ViewGroup 攔截全部事件,並設置 View 和 ViewGroup 的點擊事件
- ViewGroup 僅攔截 DOWN 事件
- ViewGroup 攔截 DOWN 事件,並設置 ViewGroup 的點擊事件
- ViewGroup 攔截 DOWN 事件,並設置 View 的點擊事件
- ViewGroup 攔截 DOWN 事件,並設置 View 和 ViewGroup 的點擊事件
- ViewGroup 僅攔截 MOVE 事件
- ViewGroup 攔截 MOVE 事件,並設置 ViewGroup 的點擊事件
- ViewGroup 攔截 MOVE 事件,並設置 View 的點擊事件
- ViewGroup 攔截 MOVE 事件,並設置 View 和 ViewGroup 的點擊事件
- ViewGroup 僅攔截 UP 事件
- ViewGroup 攔截 UP 事件,並設置 ViewGroup 的點擊事件
- ViewGroup 攔截 UP 事件,並設置 View 的點擊事件
- ViewGroup 攔截 UP 事件,並設置 View 和 ViewGroup 的點擊事件
- 僅設置 View 爲可點擊的
- 僅設置 View 的 enable 爲 false
- 設置 View 的 enable 爲 false 並且可點擊
默認狀態
運行程序,我們點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
MainActivity: onTouchEvent: ev.getAction() = ACTION_UP
tips
: 多次表示 下面那些事件依次執行 循環多次.
從以上日誌我們可以看到 DOWN 事件的傳遞路徑爲從 MainActivity.dispatchTouchEvent
→ TestLinearLayout.dispatchTouchEvent
→ TestLinearLayout.onInterceptTouchEvent
→ TestView.dispatchTouchEvent
→ TestView.onTouchEvent
→ TestLinearLayout.onTouchEvent
→ MainActivity.onTouchEvent
, 而 MOVE 和 UP 事件就只發生在 MainActivity.
因爲 View 默認 clickable 和 longClickable 都爲 false
,所以沒有消耗 DOWN 事件。
滿足上述第 1、2、4、6、7、8、11 點。
僅View設置點擊事件
我們點擊示例中的 View setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_MOVE, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestView: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_UP, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestView: onTouchEvent: ev.getAction() = ACTION_UP
因爲 TestView 消耗了 DOWN 事件,後面的每個事件都傳遞到了 TestView,我們可以看到 DOWN、MOVE、UP 每個事件都是從 MainActivity → TestLinearLayout → TestView,TestView 並沒有處理 MOVE 所以就 消失 了。
滿足上述第 1、2、4、5、6、7、11 點。
僅ViewGroup設置點擊事件
我們依次點擊示例中的 Reset 和 ViewGroup setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_UP
我們可以看到 DOWN 事件是從 MainActivity → TestLinearLayout → TestView → TestLinearLayout,而MOVE、UP事件就只從 MainActivity → TestLinearLayout.
因爲 TestView 沒有消耗 DOWN 事件,然後傳給TestLinearLayout,TestLinearLayout消耗了 DOWN 事件,所以後面的每個事件都只傳遞到了 TestLinearLayout。
滿足上述第 1、2、4、5、6、7、8、11 點。
View和ViewGroup都設置點擊事件
我們依次點擊示例中的 Reset 、View setOnClick 和 ViewGroup setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
多個
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_MOVE, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestView: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_UP, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestView: onTouchEvent: ev.getAction() = ACTION_UP
這裏和 “僅View設置點擊事件” 一致,因爲 TestView 消耗了 DOWN 事件,TestLinearLayout 就無法再消耗 DOWN 事件了。
ViewGroup設置攔截全部事件 (僅攔截,沒消耗)
我們依次點擊示例中的 Reset 和 ViewGroup Intercept All 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = true
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
MainActivity: onTouchEvent: ev.getAction() = ACTION_UP
我們可以看到 DOWN 事件傳遞路徑爲 MainActivity → TestLinearLayout,因爲DOWN 事件被攔截了,所以 TestView 收不到事件,而MOVE、UP事件就只在 MainActivity.
滿足上述第 1、3、4、5、6、7、11 點。
ViewGroup設置攔截全部事件,並設置ViewGroup的點擊事件.
我們依次點擊示例中的 Reset 、ViewGroup Intercept All 和 ViewGroup setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_UP
這裏我們可以看到 DOWN 事件傳遞路徑爲 MainActivity → TestLinearLayout,因爲TestLinearLayout消耗了 DOWN 事件,所以MOVE、UP事件也傳到了 TestLinearLayout.
滿足上述第 1、2、3、4、5、6、7、8、11 點。
ViewGroup設置攔截全部事件,並設置View的點擊事件.
我們依次點擊示例中的 Reset 、ViewGroup Intercept All 和 View setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = true
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
MainActivity: onTouchEvent: ev.getAction() = ACTION_UP
和 “ViewGroup僅設置攔截全部事件” 一致,TestLinearLayout 攔截了事件,所以 TestView 收不到事件,而TestLinearLayout 沒有消耗 DOWN 事件,後續的事件也沒有繼續傳遞到他。
ViewGroup設置攔截全部事件,並設置View和ViewGroup的點擊事件.
我們依次點擊示例中的 Reset 、ViewGroup Intercept All、 View setOnClick 和 ViewGroup setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = true
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_UP
和 “ViewGroup設置攔截全部事件,並設置ViewGroup的點擊事件” 一致,TestLinearLayout 攔截了事件,所以 TestView 收不到事件,而TestLinearLayout 消耗了 DOWN 事件,後續的事件也都繼續傳遞到他。
僅ViewGroup攔截DOWN事件
我們依次點擊示例中的 Reset 和 ViewGroup Intercept down按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestLinearLayout: intercept event = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
MainActivity: onTouchEvent: ev.getAction() = ACTION_UP
和 “ViewGroup僅設置攔截全部事件” 一致,TestLinearLayout 攔截了事件,所以 TestView 收不到事件,而TestLinearLayout 沒有消耗 DOWN 事件,後續的事件也沒有繼續傳遞到他。
ViewGroup攔截DOWN事件,並設置ViewGroup的點擊事件
我們依次點擊示例中的 Reset 、ViewGroup Intercept down 和 ViewGroup setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestLinearLayout: intercept event = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_UP
和 “ViewGroup設置攔截全部事件,並設置ViewGroup的點擊事件” 一致,TestLinearLayout 攔截了事件,所以 TestView 收不到事件,而TestLinearLayout 消耗了 DOWN 事件,後續的事件也都繼續傳遞到他。
ViewGroup攔截DOWN事件,並設置View的點擊事件
我們依次點擊示例中的 Reset 、ViewGroup Intercept down 和 View setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestLinearLayout: intercept event = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
MainActivity: onTouchEvent: ev.getAction() = ACTION_UP
和 “ViewGroup設置攔截全部事件” 一致,TestLinearLayout 攔截了事件,所以 TestView 收不到事件,而TestLinearLayout 沒有消耗 DOWN 事件,後續的事件也沒有繼續傳遞到他。
ViewGroup攔截DOWN事件,並設置View和ViewGroup的點擊事件
我們依次點擊示例中的 Reset 、ViewGroup Intercept down、View setOnClick 和 ViewGroup setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestLinearLayout: intercept event = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_UP
和 “ViewGroup攔截DOWN事件,並設置ViewGroup的點擊事件” 一致,TestLinearLayout 攔截了事件,所以 TestView 收不到事件,而TestLinearLayout 消耗了 DOWN 事件,後續的事件也都繼續傳遞到他。
僅ViewGroup攔截MOVE事件
我們依次點擊示例中的 Reset 和 ViewGroup Intercept move 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
MainActivity: onTouchEvent: ev.getAction() = ACTION_UP
和 “默認情況” 一致,因爲 TestLinearLayout 和 TestView 都沒消耗 DOWN 事件,所以後續事件都不能傳遞到它們。
ViewGroup攔截MOVE事件,並設置ViewGroup的點擊事件
我們依次點擊示例中的 Reset 、ViewGroup Intercept move 和 ViewGroup setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_UP
和 “設置ViewGroup的點擊事件” 的點擊事件一致,此時事件已經由 TestLinearLayout 處理,所以也沒回調 onInterceptTouchEvent 方法。
ViewGroup攔截MOVE事件,並設置View的點擊事件
我們依次點擊示例中的 Reset 、ViewGroup Intercept move 和 View setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_MOVE, isIntercept = false
TestLinearLayout: intercept event = ACTION_MOVE
TestView: dispatchTouchEvent: ev.getAction() = ACTION_CANCEL
TestView: onTouchEvent: ev.getAction() = ACTION_CANCEL
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_UP
MainActivity: onTouchEvent: ev.getAction() = ACTION_UP
可以看到 TestView 消耗了 DOWN 事件, 但是 MOVE 事件被 TestLinearLayout 攔截了,然後後續的 MOVE 和 UP 事件就只能傳到 TestLinearLayout 了。
而且在攔截到MOVE事件到的時候,會觸發TestView 的 ACTION_CANCEL 事件
滿足上述第 1、2、3、4、5、6、7、8、11 點。
ViewGroup攔截Move事件,並設置View和ViewGroup的點擊事件
我們依次點擊示例中的 Reset 、ViewGroup Intercept move、ViewGroup setOnClick 和 View setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_MOVE, isIntercept = false
TestLinearLayout: intercept event = ACTION_MOVE
TestView: dispatchTouchEvent: ev.getAction() = ACTION_CANCEL
TestView: onTouchEvent: ev.getAction() = ACTION_CANCEL
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_UP
和 “ViewGroup攔截MOVE事件,並設置View的點擊事件” 一致。因爲 DOWN 事件 的先傳個 TestView.
僅ViewGroup攔截UP事件
我們依次點擊示例中的 Reset 和 ViewGroup Intercept up 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
MainActivity: onTouchEvent: ev.getAction() = ACTION_UP
和 “默認” 一致,因爲 TestLinearLayout 也沒有消耗 DOWN 事件,所以就收不到 MOVE 和 UP 事件。
ViewGroup攔截UP事件,並設置ViewGroup的點擊事件
我們依次點擊示例中的 Reset 、ViewGroup Intercept up 和 ViewGroup setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_UP
這裏因爲 TestLinearLayout 消耗了 DOWN 事件, 因爲事件已經是它在處理了,也沒有調用 onInterceptTouchEvent
方法來攔截 UP 事件。
滿足上述第 1、3、4、5、6、7、11 點。
ViewGroup攔截 UP 事件,並設置View的點擊事件
我們依次點擊示例中的 Reset 、ViewGroup Intercept up 和 View setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_MOVE, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestView: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_UP, isIntercept = false
TestLinearLayout: intercept event = ACTION_UP
TestView: dispatchTouchEvent: ev.getAction() = ACTION_CANCEL
TestView: onTouchEvent: ev.getAction() = ACTION_CANCEL
這裏因爲 TestView 消耗了 DOWN 事件,而 TestLinearLayout 只攔截 UP 事件,所以前面的 DOWN 和 MOVE 一次傳遞到 TestView,然後在 UP 事件的時候,被 TestLinearLayout 攔截,然後觸發了 TestView 的 ACTION_CANCEL 事件。
滿足上述第 1、2、3、4、5、6、7、8、11 點。
ViewGroup攔截 UP 事件,並設置View和 ViewGroup 的點擊事件
我們依次點擊示例中的 Reset 、ViewGroup Intercept up 、View setOnClick 和 ViewGroup setOnClick 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
多次
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_MOVE, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_MOVE
TestView: onTouchEvent: ev.getAction() = ACTION_MOVE
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_UP, isIntercept = false
TestLinearLayout: intercept event = ACTION_UP
TestView: dispatchTouchEvent: ev.getAction() = ACTION_CANCEL
TestView: onTouchEvent: ev.getAction() = ACTION_CANCEL
同 “ViewGroup攔截 UP 事件,並設置View的點擊事件” 一致
僅設置 View 爲可點擊的
我們依次點擊示例中的 Reset 和 set view Clickable true 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_UP, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestView: onTouchEvent: ev.getAction() = ACTION_UP
同 “僅設置View的點擊事件” 一致,主要驗證第 8 點。
僅設置 View 的 enable 爲 false
我們依次點擊示例中的 Reset 、和 set view enable false 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
MainActivity: onTouchEvent: ev.getAction() = ACTION_UP
同 “默認情況” 一致,主要驗證第 9 點。
設置 View 的 enable 爲 false 並且可點擊
我們依次點擊示例中的 Reset 、set view Clickable true 和 set view enable false 按鈕,然後點擊 TestView 所在的區域,可以看到打印的日誌如下:
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_DOWN, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_DOWN
TestView: onTouchEvent: ev.getAction() = ACTION_DOWN
MainActivity: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestLinearLayout: onInterceptTouchEvent: ev.getAction() = ACTION_UP, isIntercept = false
TestView: dispatchTouchEvent: ev.getAction() = ACTION_UP
TestView: onTouchEvent: ev.getAction() = ACTION_UP
同 “僅設置View的點擊事件” 一致,主要驗證第 8、9 點。
對日誌信息的小結
這裏先簡單的小結下,通過上面的日誌我們可以看到:
默認情況下事件的傳遞順序是
Activity.dispatchTouchEvent
→ViewGroup.dispatchTouchEvent
→ViewGroup.onInterceptTouchEvent
→View.dispatchTouchEvent
→View.onTouchEvent
→ViewGroup.onTouchEvent
→Activity.onTouchEvent
, 然後後續的的事件都只走Activity.dispatchTouchEvent
→Activity.onTouchEvent
了,不會再繼續放下傳了。
就好像老闆接到一個任務,然後交給你的上級,你的上級然後交給你,你想了很久發現解決不了,就告訴上級解決不了,然後你的上級又想了很久,發現也解決不了,就反饋給老闆,然後老闆就只能自己處理這個任務了,後續類似的任務也不會給你的上級,從而你也就不會有類似的任務了。只要中間的 ViewGroup 和 View 誰處理了 down 事件, 後續的事件除了上層攔截之外都會傳到處理了 down 事件的位置上。
還是上面的例子,如果完成那個任務,那麼後續類似的任務老闆都會接下來交給你的上級,你的上級也能處理的話,他也會先交給你,如果你能處理,就讓你處理,如果你處理不了,後續類似的任務也不會分配給你了,他就自己處理了。如果 View 誰處理了 down 事件,但是後續的事件被 ViewGroup 攔截了,則後續的事件都會交給 ViewGroup 去處理。
還是上面的例子,老闆接到一個任務,然後交給你的上級,你的上級然後交給你,然後你完成了第一部分,然後你要開始休長假了,所以後續的任務你的上級就會自己處理,不會在你休假期間打擾你了。然後 View 的 enable 狀態並不會影響其接收事件。
就像你的上級叫你去開會,但是你在開另一個會,並不會因爲你在開另外一個會而不通知你。
如果有描述錯誤的,請提醒我,感謝!
以上
如果覺得不錯的話,請幫忙點個讚唄。
掃描下面的二維碼,關注我的公衆號 103Tech, 點關注,不迷路。