Android事件分發機制驗證示例

轉載請以鏈接形式標明出處:
本文出自: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 除外


示例驗證

這裏大家主要關注打印的日誌信息,看下事件分發的過程即可。

Demo源碼github地址

我們分別創建監聽上面三個方法的 ViewGroup 和 View,然後在測試 Activity 中重寫上面的方法,源碼鏈接如下:
TestLinearLayout.javaTestView.javaEventHandler.javaMainActivity.javaactivity_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.dispatchTouchEventTestLinearLayout.dispatchTouchEventTestLinearLayout.onInterceptTouchEventTestView.dispatchTouchEventTestView.onTouchEventTestLinearLayout.onTouchEventMainActivity.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設置點擊事件

我們依次點擊示例中的 ResetViewGroup 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都設置點擊事件

我們依次點擊示例中的 ResetView setOnClickViewGroup 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設置攔截全部事件 (僅攔截,沒消耗)

我們依次點擊示例中的 ResetViewGroup 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的點擊事件.

我們依次點擊示例中的 ResetViewGroup Intercept AllViewGroup 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的點擊事件.

我們依次點擊示例中的 ResetViewGroup Intercept AllView 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的點擊事件.

我們依次點擊示例中的 ResetViewGroup Intercept AllView setOnClickViewGroup 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事件

我們依次點擊示例中的 ResetViewGroup 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的點擊事件

我們依次點擊示例中的 ResetViewGroup Intercept downViewGroup 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的點擊事件

我們依次點擊示例中的 ResetViewGroup Intercept downView 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的點擊事件

我們依次點擊示例中的 ResetViewGroup Intercept downView setOnClickViewGroup 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事件

我們依次點擊示例中的 ResetViewGroup 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的點擊事件

我們依次點擊示例中的 ResetViewGroup Intercept moveViewGroup 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的點擊事件

我們依次點擊示例中的 ResetViewGroup Intercept moveView 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的點擊事件

我們依次點擊示例中的 ResetViewGroup Intercept moveViewGroup setOnClickView 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事件

我們依次點擊示例中的 ResetViewGroup 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的點擊事件

我們依次點擊示例中的 ResetViewGroup Intercept upViewGroup 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的點擊事件

我們依次點擊示例中的 ResetViewGroup Intercept upView 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 的點擊事件

我們依次點擊示例中的 ResetViewGroup Intercept upView setOnClickViewGroup 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 爲可點擊的

我們依次點擊示例中的 Resetset 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 並且可點擊

我們依次點擊示例中的 Resetset view Clickable trueset 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.dispatchTouchEventViewGroup.dispatchTouchEventViewGroup.onInterceptTouchEventView.dispatchTouchEventView.onTouchEventViewGroup.onTouchEventActivity.onTouchEvent, 然後後續的的事件都只走 Activity.dispatchTouchEventActivity.onTouchEvent了,不會再繼續放下傳了。
    就好像老闆接到一個任務,然後交給你的上級,你的上級然後交給你,你想了很久發現解決不了,就告訴上級解決不了,然後你的上級又想了很久,發現也解決不了,就反饋給老闆,然後老闆就只能自己處理這個任務了,後續類似的任務也不會給你的上級,從而你也就不會有類似的任務了。

  • 只要中間的 ViewGroup 和 View 誰處理了 down 事件, 後續的事件除了上層攔截之外都會傳到處理了 down 事件的位置上。
    還是上面的例子,如果完成那個任務,那麼後續類似的任務老闆都會接下來交給你的上級,你的上級也能處理的話,他也會先交給你,如果你能處理,就讓你處理,如果你處理不了,後續類似的任務也不會分配給你了,他就自己處理了。

  • 如果 View 誰處理了 down 事件,但是後續的事件被 ViewGroup 攔截了,則後續的事件都會交給 ViewGroup 去處理。
    還是上面的例子,老闆接到一個任務,然後交給你的上級,你的上級然後交給你,然後你完成了第一部分,然後你要開始休長假了,所以後續的任務你的上級就會自己處理,不會在你休假期間打擾你了。

  • 然後 View 的 enable 狀態並不會影響其接收事件。
    就像你的上級叫你去開會,但是你在開另一個會,並不會因爲你在開另外一個會而不通知你。


如果有描述錯誤的,請提醒我,感謝!

以上

如果覺得不錯的話,請幫忙點個讚唄。


掃描下面的二維碼,關注我的公衆號 103Tech, 點關注,不迷路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章