View 事件传递体系知识梳理(1) 事件分发机制
生活随笔
收集整理的這篇文章主要介紹了
View 事件传递体系知识梳理(1) 事件分发机制
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一、事件分發(fā)概述
1.1 事件分發(fā)的關(guān)鍵方法
對于ViewGroup來說,與事件分發(fā)相關(guān)的方法包括:
public boolean dispatchTouchEvent(MotionEvent event) public boolean onInterceptTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event) 復(fù)制代碼對于View來說,與事件分發(fā)相關(guān)的方法包括:
public boolean dispatchTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event) 復(fù)制代碼1.2 Down分發(fā)的理解
對于Down分發(fā)的過程,網(wǎng)上看了很多的例子和圖,但是都沒能好好理解,最后還是自己總結(jié)了一種方案,這個方案的核心就是染色:
- 初始的時候View樹上每個節(jié)點都是白色的。
- 從子節(jié)點的角度來看,當它被父節(jié)點調(diào)用dispatchTouchEvent時,在返回之前都會有一個返回值,如果這個返回值為真,那么它自己就會變成紅色。
- 從父節(jié)點的角度來看,當它給子節(jié)點調(diào)用dispatchTouchEvent時,它既是在嘗試給子節(jié)點進行染色,也是在嘗試給自己著色,當某個子節(jié)點的dispatchTouchEvent方法返回時,取該子節(jié)點的顏色對自己進行著色;如果遍歷完它所有的子節(jié)點,它仍然沒有變成紅色,那么調(diào)用它自己的onTouchEvent,如果返回true,那么把自己染成紅色。
- 對于一個ViewGroup/View來說,有可能染色成功只包括兩種途徑:子節(jié)點的dispatchTouchEvent返回時,取子節(jié)點的顏色對自己著色;通過自己的onTouchEvent方法來著色。并且,只有前一種途徑不成功時才會用后一種途徑。
- 只要節(jié)點變成了紅色,那么就不需要再嘗試對自己進行染色了,也就是上面那條說的:利用子節(jié)點的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; } 復(fù)制代碼在Down事件分發(fā)完后,我們可以發(fā)現(xiàn)這么個現(xiàn)象。
- 假如一個節(jié)點是紅色的,那么它最多只可能有一個紅色的子節(jié)點。
- 假如一個節(jié)點是紅色的,那么必然會有一條唯一的路徑,該路徑會通過該紅色節(jié)點連接到根節(jié)點。
- 假如一個節(jié)點是白色的,那么它所有的后代節(jié)點都一定是白色的。
1.3 Down分發(fā)的特點
對于一個沒有重寫以上關(guān)鍵方法并且位于View樹上的ViewGroup/View來說,它Down事件的分發(fā)具有以下幾個特點:
1.3.1 dispatchTouchEvent
- dispatchTouchEvent是否被回調(diào),由它的父容器決定的。
- 假如是它不是葉節(jié)點,當它的dispatchTouchEvent被調(diào)用時,它會先逆序依次調(diào)用下一級子節(jié)點的dispatchTouchEvent方法。
- 如果在以上遍歷中間有任何一個子節(jié)點的dispatchTouchEvent返回了true,那么不會繼續(xù)調(diào)用剩余未遍歷子節(jié)點的dispatchTouchEvent,并且它自身的onTouchEvent不會被回調(diào)。
1.3.2 onInterceptTouchEvent
只有ViewGroup才有,它是否被回調(diào)取決于dispatchTouchEvent是否被回調(diào)。
1.3.3onTouchEvent
onTouchEvent是由自己回調(diào)的,是否被回調(diào),必須同時滿足以下兩個條件:
- dispatchTouchEvent被回調(diào)。
- 滿足以下兩種情況之一:
- 它是整個View樹的葉節(jié)點
- 它擁有子節(jié)點,但是它所有子節(jié)點的dispatchTouchEvent都返回false。
1.4 Move/Up分發(fā)
在Move/Up事件分發(fā)的時候,其實就是根據(jù)之前著色的結(jié)果來往下傳遞事件,它的傳遞只需要遵循下面的原則:只會分發(fā)給紅色的節(jié)點,遇到白色的節(jié)點就停止往下分發(fā)。
1.5 簡單舉例
我們舉一個簡單的例子:
- 前提條件:ViewGroup1返回true。
- 過程:Root調(diào)用ViewGroup1的dispatchTouchEvent,而ViewGroup1此時是白色,因此它繼續(xù)調(diào)用它的子節(jié)點,也就是View21的dispatchTouchEvent,但是View21沒有子節(jié)點,因此它調(diào)用自己的onTouchEvent,它的dispatchTouchEvent方法返回,而此時,ViewGroup2所有的子節(jié)點都遍歷完了,它依然沒有變成紅色,因此它調(diào)用自己的onTouchEvent,由于該方法返回false,因此它也返回了,并且在返回時依然是白色。接下來Root取ViewGroup2的顏色對自己著色,著色完成之后發(fā)現(xiàn)自己仍然是白色,那么它就繼續(xù)調(diào)用有可能使自己染色成功的方法,ViewGroup1的dispatchTouchEvent,由于它的dispatchTouchEvent返回true,因此它會把自己染成紅色,由于它已經(jīng)變成紅色了,那么它也沒有權(quán)利對子節(jié)點進行染色,因此它的dispatchTouchEvent返回,Root收到返回值時,取ViewGroup1的顏色對自己進行著色,結(jié)果它發(fā)現(xiàn)自己是紅色了,那么Root也不會調(diào)用任何可能對自己染色的方法,而是直接返回了。
- 結(jié)果:ROOT和ViewGroup1變?yōu)榧t色節(jié)點。
二、示例
只要理解了上面的思想,其實各種情況都可以對應(yīng)到,下面的例子只是為了讓大家能夠證明自己的想法是否正確罷了,所以就不過多解釋了:
<?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> 復(fù)制代碼2.1 不改變?nèi)魏魏瘮?shù)的返回值
2.2 ViewGroup2的dispatchTouchEvent返回false
2.3 ViewGroup2的dispatchTouchEvent返回true
2.4 ViewGroup1下的孩子節(jié)點View2返回了false
2.5 ViewGroup1下的孩子節(jié)點View2返回了true
2.6 ViewGroup2的onTouchEvent返回了false
2.7 ViewGroup2的onTouchEvent返回了true
2.8 ViewGroup1下的孩子節(jié)點View2返回了false
2.9 ViewGroup1下的孩子節(jié)點View2返回了true
總結(jié)
以上是生活随笔為你收集整理的View 事件传递体系知识梳理(1) 事件分发机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RecyclerView 数据预取
- 下一篇: BZOJ1970 [Ahoi2005]