1

前言

当我们自定义一些布局空间时经常需要处理Touch事件,当布局之间存在嵌套关系时,事件又是如何在父子控件之间传递的呢?

设计

我们自定义了两个RelativeLayout,分别称为FatherView和ChildView,其中ChildView是嵌套在FatherView之中,代码如下。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.movendemo.MainActivity" >

    <com.moven.ui.FatherView
        android:id="@+id/grandpa"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_blue_light">
          
        <com.moven.ui.ChildView
            android:id="@+id/father"
            android:layout_width="450dp"
            android:layout_height="450dp"
            android:layout_centerInParent="true"
            android:background="@android:color/holo_purple">
           
        </com.moven.ui.ChildView>
        
    </com.moven.ui.FatherView>

</RelativeLayout>

在FatherView和ChildView中,分别override以下三个方法:

public boolean dispatchTouchEvent(MotionEvent ev);

public boolean onInterceptTouchEvent(MotionEvent ev);

public boolean onTouchEvent(MotionEvent event);

接下来看看这三个方法的调用流程和作用。

dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent

先看看接口的描述。

图片描述

该接口返回true表示事件被当前的view处理了,返回false表示当前view没有处理该事件。

图片描述

实现onInterceptTouchEvent方法可以watch子控件的事件,该方法返回true时,子控件的事件会被父控件“偷”掉,就是被拦截掉。

图片描述

onTouch比较简单,就是用来处理事件的,返回true表示事件已经被处理了,返回false表示事件没有被处理,和dispatchTouchEvent一样。

分析

我们首先单独看dispatchTouchEvent的事件传递流程,为此,我们设置onInterceptTouchEvent返回false,onTouch返回true。

dispatchTouchEvent

通过对FatherView和ChildView的dispatchTouchEvent方法返回值进行true/false的组合,得出如下结果:

(1)ACTION_DOWN的传递

图片描述

ACTION_DOWN事件会先从父控件至子控件的方向调用dispatchTouchEvent方法,在传递至根控件后再调用onTouch方法向上回溯,每一级onTouch方法调用完后事件是否继续向上回溯依赖于本级dispatchTouchEvent的返回值,dispatch返回true表示事件已处理,不再向上传递,返回false事件会回溯至父控件的onTouch方法。

(2)ACTION_MOVE和ACTION_UP

图片描述

在每一级控件中,ACTION_MOVE和ACTION_UP事件的传递依赖于ACTION_DOWN事件传递时dispatchTouchEvent的返回值,如果ACTION_DOWN调用dispatch时返回false,那么MOVE、UP事件将不会再调用dispatchTouchEvent方法,也就是说该控件将不会收到MOVE、UP事件。事件回溯和ACTION_DOWN流程一样。

onInterceptTouchEvent

在dispatchTouchEvent事件传递流程中加入onInterceptTouchEvent的true/false返回值组合,得出如下结果:

图片描述

每级控件的onInterceptTouchEvent在dispatchTouchEvent之后调用,如果intercept方法返回true,事件将被这一级控件拦截掉,之后直接交给该控件的onTouch方法处理,不会再传递给子控件的dispatch方法。并且,当事件被某一级控件拦截掉之后,后续事件经过dispatch方法后将直接交给onTouch方法,不会再重复调用intercept方法。

onTouchEvent

onTouchEvent返回true/false的效果同dispatchTouchEvent类似,如果onTouchEvent返回false,该方法后续将不会收到ACTION_MOVE和ACTION_UP事件。

总结

从总体流程来看,事件传递是U型的,先从父控件经过dispatchTouchEvent、interceptTouchEvent正向传递至子控件,再从子控件通过onTouchEvent反向回溯至父控件:

图片描述

通过重写这几个方法,可以实现对事件传递流程的控制。


moven_j
163 声望1 粉丝

贱也是有文化的体现