前言
当我们自定义一些布局空间时经常需要处理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反向回溯至父控件:
通过重写这几个方法,可以实现对事件传递流程的控制。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。