View 事件传递体系知识梳理(1) 事件分发机制
生活随笔
收集整理的這篇文章主要介紹了
View 事件传递体系知识梳理(1) 事件分发机制
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、事件分發概述
1.1 事件分發的關鍵方法
對于ViewGroup來說,與事件分發相關的方法包括:
public boolean dispatchTouchEvent(MotionEvent event) public boolean onInterceptTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event) 復制代碼對于View來說,與事件分發相關的方法包括:
public boolean dispatchTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event) 復制代碼1.2 Down分發的理解
對于Down分發的過程,網上看了很多的例子和圖,但是都沒能好好理解,最后還是自己總結了一種方案,這個方案的核心就是染色:
- 初始的時候View樹上每個節點都是白色的。
- 從子節點的角度來看,當它被父節點調用dispatchTouchEvent時,在返回之前都會有一個返回值,如果這個返回值為真,那么它自己就會變成紅色。
- 從父節點的角度來看,當它給子節點調用dispatchTouchEvent時,它既是在嘗試給子節點進行染色,也是在嘗試給自己著色,當某個子節點的dispatchTouchEvent方法返回時,取該子節點的顏色對自己進行著色;如果遍歷完它所有的子節點,它仍然沒有變成紅色,那么調用它自己的onTouchEvent,如果返回true,那么把自己染成紅色。
- 對于一個ViewGroup/View來說,有可能染色成功只包括兩種途徑:子節點的dispatchTouchEvent返回時,取子節點的顏色對自己著色;通過自己的onTouchEvent方法來著色。并且,只有前一種途徑不成功時才會用后一種途徑。
- 只要節點變成了紅色,那么就不需要再嘗試對自己進行染色了,也就是上面那條說的:利用子節點的dispatchTouchEvent來染色或者通過自己的onTouch來染色,它會直接返回。
從偽代碼來看:
public Color color = white;public boolean dispatchTouchEvent(MotionEvent event) {int childCount = getChildCount;for (i = childCount - 1; i >= 0; i--) {View child = getChildAt(i);boolean result = child.dispatchTouchEvent(event);if (result) {color = RED;return true;}}boolean touchRes = onTouchEvent(event);if (touchRes) {color = RED;}return touchRes; } 復制代碼在Down事件分發完后,我們可以發現這么個現象。
- 假如一個節點是紅色的,那么它最多只可能有一個紅色的子節點。
- 假如一個節點是紅色的,那么必然會有一條唯一的路徑,該路徑會通過該紅色節點連接到根節點。
- 假如一個節點是白色的,那么它所有的后代節點都一定是白色的。
1.3 Down分發的特點
對于一個沒有重寫以上關鍵方法并且位于View樹上的ViewGroup/View來說,它Down事件的分發具有以下幾個特點:
1.3.1 dispatchTouchEvent
- dispatchTouchEvent是否被回調,由它的父容器決定的。
- 假如是它不是葉節點,當它的dispatchTouchEvent被調用時,它會先逆序依次調用下一級子節點的dispatchTouchEvent方法。
- 如果在以上遍歷中間有任何一個子節點的dispatchTouchEvent返回了true,那么不會繼續調用剩余未遍歷子節點的dispatchTouchEvent,并且它自身的onTouchEvent不會被回調。
1.3.2 onInterceptTouchEvent
只有ViewGroup才有,它是否被回調取決于dispatchTouchEvent是否被回調。
1.3.3onTouchEvent
onTouchEvent是由自己回調的,是否被回調,必須同時滿足以下兩個條件:
- dispatchTouchEvent被回調。
- 滿足以下兩種情況之一:
- 它是整個View樹的葉節點
- 它擁有子節點,但是它所有子節點的dispatchTouchEvent都返回false。
1.4 Move/Up分發
在Move/Up事件分發的時候,其實就是根據之前著色的結果來往下傳遞事件,它的傳遞只需要遵循下面的原則:只會分發給紅色的節點,遇到白色的節點就停止往下分發。
1.5 簡單舉例
我們舉一個簡單的例子:
- 前提條件:ViewGroup1返回true。
- 過程:Root調用ViewGroup1的dispatchTouchEvent,而ViewGroup1此時是白色,因此它繼續調用它的子節點,也就是View21的dispatchTouchEvent,但是View21沒有子節點,因此它調用自己的onTouchEvent,它的dispatchTouchEvent方法返回,而此時,ViewGroup2所有的子節點都遍歷完了,它依然沒有變成紅色,因此它調用自己的onTouchEvent,由于該方法返回false,因此它也返回了,并且在返回時依然是白色。接下來Root取ViewGroup2的顏色對自己著色,著色完成之后發現自己仍然是白色,那么它就繼續調用有可能使自己染色成功的方法,ViewGroup1的dispatchTouchEvent,由于它的dispatchTouchEvent返回true,因此它會把自己染成紅色,由于它已經變成紅色了,那么它也沒有權利對子節點進行染色,因此它的dispatchTouchEvent返回,Root收到返回值時,取ViewGroup1的顏色對自己進行著色,結果它發現自己是紅色了,那么Root也不會調用任何可能對自己染色的方法,而是直接返回了。
- 結果:ROOT和ViewGroup1變為紅色節點。
二、示例
只要理解了上面的思想,其實各種情況都可以對應到,下面的例子只是為了讓大家能夠證明自己的想法是否正確罷了,所以就不過多解釋了:
<?xml version="1.0" encoding="utf-8"?> <com.example.lizejun.repoviews.LogFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:tag="ViewGroup$Root"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.lizejun.repoviews.MainActivity"><com.example.lizejun.repoviews.LogFrameLayoutandroid:tag="ViewGroup1"android:layout_width="match_parent"android:layout_height="match_parent"><com.example.lizejun.repoviews.LogTextViewandroid:tag="ViewGroup1$View1"android:layout_width="match_parent"android:layout_height="match_parent" /><com.example.lizejun.repoviews.LogTextViewandroid:tag="ViewGroup1$View2"android:layout_width="match_parent"android:layout_height="match_parent" /></com.example.lizejun.repoviews.LogFrameLayout><com.example.lizejun.repoviews.LogFrameLayoutandroid:tag="ViewGroup2"android:layout_width="match_parent"android:layout_height="match_parent"><com.example.lizejun.repoviews.LogTextViewandroid:tag="ViewGroup2$View"android:layout_width="match_parent"android:layout_height="match_parent"/></com.example.lizejun.repoviews.LogFrameLayout> </com.example.lizejun.repoviews.LogFrameLayout> 復制代碼2.1 不改變任何函數的返回值
2.2 ViewGroup2的dispatchTouchEvent返回false
2.3 ViewGroup2的dispatchTouchEvent返回true
2.4 ViewGroup1下的孩子節點View2返回了false
2.5 ViewGroup1下的孩子節點View2返回了true
2.6 ViewGroup2的onTouchEvent返回了false
2.7 ViewGroup2的onTouchEvent返回了true
2.8 ViewGroup1下的孩子節點View2返回了false
2.9 ViewGroup1下的孩子節點View2返回了true
總結
以上是生活随笔為你收集整理的View 事件传递体系知识梳理(1) 事件分发机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RecyclerView 数据预取
- 下一篇: BZOJ1970 [Ahoi2005]