android 事件的分发和消费机制,有这一篇就够了

android 事件的分发和消费机制,有这一篇就够了

一、简介

  Activity或View类的onTouchEvent()回调函数会接收到touch事件,一个完整的手势是从ACTION_DOWN开始,到ACTION_UP结束。简单的情况下,我们只需要在onTouchEvent()中写个switch case语句,处理各种事件(Touch Down、 Touch Move、 Touch Up等),但是比较复杂的动作就需要更多的处理了。

ViewGroup作为一个parent是可以截获传向它的child的touch事件的。如果一个ViewGroup的onInterceptTouchEvent()方法返回true,说明Touch事件被截获,子View不再接收到Touch事件,而是转向本ViewGroup的 onTouchEvent()方法处理。从Down开始,之后的Move,Up都会直接在onTouchEvent()方法中处理。先前还在处理touch event的child view将会接收到一个 ACTION_CANCEL。如果onInterceptTouchEvent()返回false,则事件会交给child view处理。

 Android中提供了ViewGroup、View、Activity三个层次的Touch事件处理。处理过程是按照Touch事件从上到下传递,再按照是否消费的返回值,从下到上返回,即如果View的onTouchEvent返回false,将会向上传给它的parent的ViewGroup,如果ViewGroup不处理,将会一直向上返回到Activity。

 总结:即管道式向下分发,然后冒泡式向上处理。

二、相关方法介绍

(一).方法与控件的对应关系

Android 中与Touch事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能够响应这些方法的控件包括:ViewGroup、View、Activity,方法与控件的对应关系如下表所示:

Touch事件相关方法 方法功能 ViewGroup View Activity
public boolean dispatchTouchEvent(MotionEvent ev) 事件分发 Yes Yes Yes
public boolean onInterceptTouchEvent(MotionEvent ev) 事件拦截 Yes No No
public boolean onTouchEvent(MotionEvent ev) 事件响应 Yes Yes Yes

从这张表中我们可以看到 ViewGroup 和 View 对与 Touch 事件相关的三个方法均能响应,而 Activity 对 onInterceptTouchEvent(MotionEvent ev) 也就是事件拦截不进行响应。另外需要注意的是 View 对 onInterceptTouchEvent(MotionEvent ev) 的响应的前提是可以向该 View 中添加子 View,如果当前的 View 已经是一个最小的单元 View(比如 TextView),那么就无法向这个最小 View 中添加子 View,也就无法向子 View 进行事件的拦截,所以它没有 onInterceptTouchEvent(MotionEvent ev)。

(二).各个方法的作用
方法 说明
dispatchTouchEvent() 用来分派事件。其中调用了onInterceptTouchEvent()和onTouchEvent(),一般不重写该方法
onInterceptTouchEvent() 用来拦截事件。ViewGroup类中的源码实现就是{return false;}表示不拦截该事件,事件将向下传递(传递给其子View);若手动重写该方法,使其返回true则表示拦截,事件将终止向下传递,事件由当前ViewGroup类来处理,就是调用该类的onTouchEvent()方法
onTouchEvent() 用来处理事件。返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View);返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理

注:ViewGroup的某些子类(GridView、ScrollView…)重写了onInterceptTouchEvent()方法,当发生ACTION_MOVE事件时,返回true进行拦截。

三、Touch 事件分析

(一)、事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

Touch 事件发生时 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法会以管道方式(从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递)将事件传递给最外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由该 View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。

(二)、事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

在外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法返回系统默认的 super.dispatchTouchEvent(ev) 情况下,事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件拦截逻辑如下:
1. 如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理;
2. 如果 onInterceptTouchEvent 返回 false,则表示将事件放行,当前 View 上的事件会被传递到子 View 上,再由子 View 的 dispatchTouchEvent 来开始这个事件的分发;
3. 如果 onInterceptTouchEvent 返回 super.onInterceptTouchEvent(ev),事件默认不会被拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理。

(三)、事件响应:public boolean onTouchEvent(MotionEvent ev)

在 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev) 并且 onInterceptTouchEvent 返回 true 或返回 super.onInterceptTouchEvent(ev) 的情况下 onTouchEvent 会被调用。onTouchEvent 的事件响应逻辑如下:
1. 如果事件传递到当前 View 的 onTouchEvent 方法,而该方法返回了 false,那么这个事件会从当前 View 向上传递,并且都是由上层 View 的 onTouchEvent 来接收,如果传递到上面的 onTouchEvent 也返回 false,这个事件就会“消失”,而且接收不到下一次事件。
2. 如果返回了 true 则会接收并消费该事件。
3. 如果返回 super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false 时,那么这个事件会从这个view 往上传递,都是onTouchEvent 来接收。如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。

四、示例,认真看看下面每条输出日志,想想怎么分发消费的会有收获

这里写图片描述
注:布局就是一个LinearLayout嵌套了一个Textview。

以下操作都是点击的Textview

 1.无拦截,无消费
 Activity: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------onInterceptTouchEvent---action = 0
 View: ---------dispatchTouchEvent---action = 0
 View: ---------onTouchEvent---action = 0
 ViewGroup: ---------onTouchEvent---action = 0
 Activity: ---------onTouchEvent---action = 0
 Activity: ---------dispatchTouchEvent---action = 1
 Activity: ---------onTouchEvent---action = 1

 2、ViewGroup拦截,无消费
 Activity: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------onInterceptTouchEvent---action = 0
 ViewGroup: ---------onTouchEvent---action = 0
 Activity: ---------onTouchEvent---action = 0
 Activity: ---------dispatchTouchEvent---action = 1
 Activity: ---------onTouchEvent---action = 1

 3、无拦截,View消费
 Activity: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------onInterceptTouchEvent---action = 0
 View: ---------dispatchTouchEvent---action = 0
 View: ---------onTouchEvent---action = 0
 Activity: ---------dispatchTouchEvent---action = 1
 ViewGroup: ---------dispatchTouchEvent---action = 1
 ViewGroup: ---------onInterceptTouchEvent---action = 1
 View: ---------dispatchTouchEvent---action = 1
 View: ---------onTouchEvent---action = 1

 4、无拦截,仅有ViewGroup消费
 Activity: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------onInterceptTouchEvent---action = 0
 View: ---------dispatchTouchEvent---action = 0
 View: ---------onTouchEvent---action = 0
 ViewGroup: ---------onTouchEvent---action = 0
 Activity: ---------dispatchTouchEvent---action = 1
 ViewGroup: ---------dispatchTouchEvent---action = 1
 ViewGroup: ---------onTouchEvent---action = 1

 5、无拦截,Activity消费
 Activity: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------dispatchTouchEvent---action = 0
 ---------onInterceptTouchEvent---action = 0
 View: ---------dispatchTouchEvent---action = 0
 ---------onTouchEvent---action = 0
 ViewGroup: ---------onTouchEvent---action = 0
 Activity: ---------onTouchEvent---action = 0
 Activity: ---------dispatchTouchEvent---action = 1
 ---------onTouchEvent---action = 1

 六、无拦截、无消费,仅在ViewGroup上设置长按监听和单击监听,执行单击动作后
 Activity: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------dispatchTouchEvent---action = 0
 ViewGroup: ---------onInterceptTouchEvent---action = 0
 View: ---------dispatchTouchEvent---action = 0
 ---------onTouchEvent---action = 0
 ViewGroup: ---------onTouchEvent---action = 0
 Activity: ---------dispatchTouchEvent---action = 1
 ViewGroup: ---------dispatchTouchEvent---action = 1
 ---------onTouchEvent---action = 1

如你有收获,不妨给个好评,那将是我最大的动力。

猜你喜欢

转载自blog.csdn.net/ZMYHH323/article/details/80446602