View 事件傳遞體系知識梳理(1) 事件分發機制

1、事件分發概述

1.1 事件分發的關鍵方法

對於ViewGroup來講,與事件分發相關的方法包括:android

public boolean dispatchTouchEvent(MotionEvent event)
public boolean onInterceptTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
複製代碼

對於View來講,與事件分發相關的方法包括:bash

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來染色,它會直接返回。

從僞代碼來看:ui

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事件分發完後,咱們能夠發現這麼個現象。spa

  • 假如一個節點是紅色的,那麼它最多隻可能有一個紅色的子節點。
  • 假如一個節點是紅色的,那麼必然會有一條惟一的路徑,該路徑會經過該紅色節點鏈接到根節點。
  • 假如一個節點是白色的,那麼它全部的後代節點都必定是白色的。

1.3 Down分發的特色

對於一個沒有重寫以上關鍵方法而且位於View樹上ViewGroup/View來講,它Down事件的分發具備如下幾個特色:code

1.3.1 dispatchTouchEvent

  • dispatchTouchEvent是否被回調,由它的父容器決定的。
  • 假如是它不是葉節點,當它的dispatchTouchEvent被調用時,它會先逆序依次調用下一級子節點的dispatchTouchEvent方法。
  • 若是在以上遍歷中間有任何一個子節點的dispatchTouchEvent返回了true,那麼不會繼續調用剩餘未遍歷子節點的dispatchTouchEvent,而且它自身的onTouchEvent不會被回調。

1.3.2 onInterceptTouchEvent

只有ViewGroup纔有,它是否被回調取決於dispatchTouchEvent是否被回調。xml

1.3.3onTouchEvent

onTouchEvent由本身回調的,是否被回調,必須同時知足如下兩個條件:事件

  • dispatchTouchEvent被回調。
  • 知足如下兩種狀況之一:
  • 它是整個View樹的葉節點
  • 它擁有子節點,可是它全部子節點的dispatchTouchEvent都返回false

1.4 Move/Up分發

Move/Up事件分發的時候,其實就是根據以前着色的結果來往下傳遞事件,它的傳遞只須要遵循下面的原則:只會分發給紅色的節點,遇到白色的節點就中止往下分發ip

1.5 簡單舉例

咱們舉一個簡單的例子: utf-8

  • 前提條件:ViewGroup1返回true
  • 過程:Root調用ViewGroup1dispatchTouchEvent,而ViewGroup1此時是白色,所以它繼續調用它的子節點,也就是View21dispatchTouchEvent,可是View21沒有子節點,所以它調用本身的onTouchEvent,它的dispatchTouchEvent方法返回,而此時,ViewGroup2全部的子節點都遍歷完了,它依然沒有變成紅色,所以它調用本身的onTouchEvent,因爲該方法返回false,所以它也返回了,而且在返回時依然是白色。接下來RootViewGroup2的顏色對本身着色,着色完成以後發現本身仍然是白色,那麼它就繼續調用有可能使本身染色成功的方法,ViewGroup1dispatchTouchEvent,因爲它的dispatchTouchEvent返回true,所以它會把本身染成紅色,因爲它已經變成紅色了,那麼它也沒有權利對子節點進行染色,所以它的dispatchTouchEvent返回,Root收到返回值時,取ViewGroup1的顏色對本身進行着色,結果它發現本身是紅色了,那麼Root也不會調用任何可能對本身染色的方法,而是直接返回了。
  • 結果:ROOTViewGroup1變爲紅色節點。

2、示例

只要理解了上面的思想,其實各類狀況均可以對應到,下面的例子只是爲了讓你們可以證實本身的想法是否正確罷了,因此就不過多解釋了:

<?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.LogFrameLayout
        android:tag="ViewGroup1"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <com.example.lizejun.repoviews.LogTextView
            android:tag="ViewGroup1$View1"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        <com.example.lizejun.repoviews.LogTextView
            android:tag="ViewGroup1$View2"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        
    </com.example.lizejun.repoviews.LogFrameLayout>
    
    <com.example.lizejun.repoviews.LogFrameLayout
        android:tag="ViewGroup2"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <com.example.lizejun.repoviews.LogTextView
            android: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 ViewGroup2dispatchTouchEvent返回false

2.3 ViewGroup2dispatchTouchEvent返回true

2.4 ViewGroup1下的孩子節點View2返回了false

2.5 ViewGroup1下的孩子節點View2返回了true

2.6 ViewGroup2onTouchEvent返回了false

2.7 ViewGroup2onTouchEvent返回了true

2.8 ViewGroup1下的孩子節點View2返回了false

2.9 ViewGroup1下的孩子節點View2返回了true

相關文章
相關標籤/搜索