您的位置:首页 > 移动开发 > Android开发

Android 触摸事件分发过程

2016-03-03 22:40 501 查看

前言

这是demo 链接

Android 中 View 的事件传递是一个老生常谈的问题,但也是学习 Android 的重点和难点。本人将分三篇来分享 Android 中的事件传递。

通过 demo 分析 Android 事件的传递过程

通过源码分析 Android 事件的传递过程

运用所学解决 Android 中滑动冲突

MotionEvent

Android 中事件的传递其实传递的就是 MotionEvent 对象。MotionEvent 中封装了事件的各种状态和属性。最常用的最典型的有如下几种:

ACTION_DOWN ———————手指刚接触屏幕

ACTION_MOVE ———————手指在屏幕上移动

ACTION_UP ———————手指从屏幕上松开的一瞬间

Android 事件传递相关方法

public boolean dispatchTouchEvent(MotionEvent ev) 负责事件的分发

public boolean onInterceptTouchEvent(MotionEvent ev) 负责事件拦截

public boolean onTouchEvent(MotionEvent event) 负责事件的处理

在 ViewGroup 中有上述三个方法,但是在 View 中没有 onInterceptTouchEvent 方法。因为 View 没有子 View,所以就不牵扯到事件的拦截。

编写 demo

自定义相关 View

由于 LinearLayout 继承 ViewGroup,我们只需新建类 CustomLinearLayout 继承 LinearLayout。重写上述三个方法,如下

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "======dispatchTouchEvent======ACTION_DOWN==");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "======dispatchTouchEvent======ACTION_MOVE==");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "======dispatchTouchEvent======ACTION_UP==");
break;
}
return super.dispatchTouchEvent(ev);}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "======onInterceptTouchEvent======ACTION_DOWN==");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "======onInterceptTouchEvent======ACTION_MOVE==");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "======onInterceptTouchEvent======ACTION_UP==");
break;
}
return super.onInterceptTouchEvent(ev);}

@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "======onTouchEvent======ACTION_DOWN==");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "======onTouchEvent======ACTION_MOVE==");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "======onTouchEvent======ACTION_UP==");
break;
}
return super.onTouchEvent(event);}


同样的新建类 CustomTextView 继承 TextView,重写 dispatchTouchEvent 和 onTouchEvent。同样重写 MainActivity 中的 dispatchTouchEvent 和 onTouchEvent。

布局文件

<com.cuifei.test.CustomLinearLayout 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"
>
<com.cuifei.test.CustomButton
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:text="CustomButton" />


查看运行结果

事件分发 Log





由图事件分发 Log 可以看出 ACTION_DOWN 传递的顺序示意图,如下图:

Android 事件分发示意图





在这之间我们并没有消费事件,由此可以看到事件从最外层逐级向内层传递,如果在这之间事件没有被消费掉,事件将由内层控件逐级向外层控件传递。

以 ACTION_DOWN 为例,详细分析 Android 事件传递的流程。 首先,MainActivity 捕获到 ACTION_DOWN 事件,并由 MainActivity 的 dispatchTouchEvent 方法分发给其子控件。因为 CustomLinearLayout 是 MainActivity 的子控件(我们暂且这么认为),所以 ACTION_DOWN 被分发给 CustomLinearLayout 并由 CustomLinearLayout 的 dispatchTouchEvent 继续分发。由于 CustomLinearLayout 是 ViewGroup 能够对事件进行拦截,所以 ACTION_DOWN 被分发给 CustomLinearLayout 的 onInterceptTouchEvent 方法。如果 onInterceptTouchEvent 返回 true 则拦截,否则不拦截。这里 CustomLinearLayout 的 onInterceptTouchEvent 返回 false,不对 ACTION_DOWN 拦截。所以 ACTION_DOWN 继续被分发给 CustomLinearLayout 的子控件 CustomTextView ,并由 CustomTextView 的 dispatchTouchEvent 进行分发。因为 CustomTextView 是 View 并且是最内层的 View ,所以 ACTION_DOWN 被分发给 CustomTextView 的 onTouchEvent 方法进行处理。本 demo 中 ACTION_DOWN 没有被 CustomTextView 的 onTouchEvent 处理掉,所以 ACTION_DOWN 向上传递给 CustomLinearLayout 的 onTouchEvent 。而 CustomLinearLayout 的 onTouchEvent 也没有处理 ACTION_DOWN,所以 ACTION_DOWN 继续向上传递给 MainActivity 的 onTouchEvent 。而 MainActivity 的 onTouchEvent 也没有处理,至此 ACTION_DOWN 将被释放,ACTION_DOWN 事件分发结束。

由图 事件分发 Log 我们还可以看出,ACTION_MOVE 和 ACTON_UP 并没有向内层控件传递,而是由 MainActivity 自己分发和处理。这说明 ACTION_MOVE 和 ACTON_UP 只传递到消费 ACTION_DOWN 的控件。为了验证这个我们将 CustomLinearLayout.onTouchEvent 的返回值改为 true 表示事件被 CustomLinearLayout 处理。

事件拦截Log





由 事件拦截Log可以看出 ACTION_MOVE 和 ACIONT_UP 只传递到了 CustomLinearLayout 这说明我们的上述观点是正确的。

由上面的代码可以看到和 onTouchEvent 方法一样,dispatchTouchEvent 和 onInterceptTouchEvent 方法也有一个 boolean 类型的返回值,这个返回值分别代表什么意思呢?

onInterceptTouchEvent 返回值为 true 时;表示事件被拦截,不在向内层控件传递。反之则向内层控件传递。

dispatchTouchEvent 返回 true 表示事件被 dispatchTouchEvent 自身处理消耗掉。至此,事件已经完结;返回 false 不再继续进行分发,并交由上层控件的onTouchEvent 方法进行处理;返回 super.dispatchTouchEvent(ev) 时,事件将继续传递。

最后两条结论的 log 将不再贴出,请读者自己验证。

最后给出本 demo 中事件分发的详细流程图,如下图:

Android 事件分发流程图



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 事件分发