安卓View——事件扥发

文章目录

目录

一、View中事件的基本概念

1.1什么是安卓View中的事件?

1.2 安卓View中事件的类型

1.3 为什么需要了解View的事件分发机制

1.4 事件分发是什么?

二、事件分发中的核心方法

三、事件分发的具体流程

总结


一、View中事件的基本概念

1.1什么是安卓View中的事件?

我们在使用安卓App的过程中,通常触摸屏幕是必不可少的,用户在触摸屏幕的过程中就会产生一系列事件,比如:手指按下屏幕、手指在屏幕上移动、手指抬起屏幕,这些都叫事件,这些事件的详细内容会被封装成一个对象(MotionEvent),内容包括事件发生的类型、坐标、时间等等,下面是一个利用toString方法打印的MotionEvent的具体内容:

MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=1079.9609, y[0]=283.9746, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=43066453, downTime=43066453, deviceId=0, source=0x1002 }

1.2 安卓View中事件的类型

在View中我们常说的事件通常都是指的Touch事件,常见的Touch事件种类有以下几种:

  1. MotionEvent.ACTION_DOWN 手指按下View
  2. MotionEvent.ACTION_MOVE 手指在View上移动
  3. MotionEvent.ACTION_UP 手指从View上抬起
  4. MotionEvent.ACTION_CANCEL 结束事件(非正常结束)

需要说明的是,关于MOVE事件,是发生在DOWN事件与UP事件之间,对于一次屏幕点击,DOWN事件只会产生一次,但是在手指移动的过程中,可能产生很多个MOVE,因为只要手指在屏幕上移动的距离超过一定的阈值,就会产生一次MOVE事件,一直移动就一直产生,直到手指抬起屏幕产生UP事件位置。

另一个需要说明的是,通常情况下,手指按下——移动——抬起的过程是一个完整的事件序列的正常结束,这种情况不会产生CACNCEL事件。如果手指按下之后,还没有抬起,就由于某些特殊原因(例如事件被上层拦截)导致事件序列提前结束,才会产生CACNCEL事件。

在MotionEvent的源码中我们还能够看到更多种类的事件,这里不再一一列出,因为,无论你的手指在屏幕上搞多么骚的操作,本质上都是按下——移动——抬起的过程,只不过你可能有多根手指一起搞骚操作,也可能是只是你移动的轨迹比较诡异仅此而已。以下是截取的部分其他事件:

ds of the view hierarchy, it will not get dispatched to
 * any children of a ViewGroup by default. Therefore,
 * movements with ACTION_OUTSIDE should be handled in either the
 * root {@link View} or in the appropriate {@link Window.Callback}
 * (e.g. {@link android.app.Activity} or {@link android.app.Dialog}).
 * </p>
 */
public static final int ACTION_OUTSIDE          = 4;

/**
 * Constant for {@link #getActionMasked}: A non-primary pointer has gone down.
 * <p>
 * Use {@link #getActionIndex} to retrieve the index of the pointer that changed.
 * </p><p>
 * The index is encoded in the {@link #ACTION_POINTER_INDEX_MASK} bits of the
 * unmasked action returned by {@link #getAction}.
 * </p>
 */
public static final int ACTION_POINTER_DOWN     = 5;

/**
 * Constant for {@link #getActionMasked}: A non-primary pointer has gone up.
 * <p>
 * Use {@link #getActionIndex} to retrieve the index of the pointer that changed.
 * </p><p>
 * The index is encoded in the {@link #ACTION_POINTER_INDEX_MASK} bits of the
 * unmasked action returned by {@link #getAction}.
 * </p>
 */
public static final int ACTION_POINTER_UP       = 6;

/**
 * Constant for {@link #getActionMasked}: A change happened but the pointer
 * is not down (unlike {@link #ACTION_MOVE}).  The motion contains the most
 * recent point, as well as any intermediate points since the last
 * hover move event.
 * <p>
 * This action is always delivered to the window or view under the pointer.
 * </p><p>
 * This action is not a touch event so it is delivered to
 * {@link View#onGenericMotionEvent(MotionEvent)} rather than
 * {@link View#onTouchEvent(MotionEvent)}.
 * </p>
 */

1.3 为什么需要了解View的事件分发机制

我们在上一篇博客中说过,View是树状结构,每个ViewGroup中可能包含了好多个子View,也不排除这些子View有重叠的部分,如果我们刚好点击了某块重叠的区域,那么到底由哪个View来处理这个事件呢?所以我们需要对View的事件分发机制有一个了解。

1.4 事件分发是什么?

在产生事件后,该事件会在Activity、View、ViewGroup中进行传递,最后按照一定的规则,决定具体由哪个部分来处理事件,这个过程就是事件的分发。产生事件后,会按照从上往下的顺序,首先是到达Activity,然后是ViewGroup以及众多子View,每层都有机会去处理事件,如果某一层处理了这个事件,则不再往下传递,若一直到最下层都没处理,则事件又会从下往上一直回传到Activity。

二、事件分发中的核心方法

事件分发中,有以下三个核心方法需要了解:

dispathchTouchEvent:顾名思义,该方法就是用来分发触摸事件,如果事件能够传递给当前View,那么此方法一定会被调用,该方法会有一个布尔型返回值,表示是否消耗当前事件

onInterceptTouchEvent:该方法用于判断是否拦事件

onTouchEvent该方法是处理事件具体逻辑的方法,会有一个布尔型返回值,表示是否消耗当前事件

事件分发的大致流程就也就是上面所说的分发事件—>拦截事件—>处理事件,不过上述三个方法的并不是在所有时候都会调用:

Activity涉及到的方法:dispatchTouchEvent()、onTouchEvent()

ViewGroup涉及到的方法:dispatchTouchEvent()、onInterceptTouchEvent()

View涉及到的方法:dispatchTouchEvent()、onTouchEvent()

上述三个方法的关系可以用伪代码表示如下:

public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean consume = false;
        if (onInterceptTouchEvent(ev)) {
                consume = onTouchEvent(ev);
        } else {
                consume = child.dispatchTouchEvent(ev);
        }
        return consume;
    }

对于一个ViewGroup来说, 点击事件产生后,首先会传递给它, 这时其dispatchTouchEvent会被调用;如果这个ViewGroup的onInterceptTouchEvent方法 返回true就表示它要拦截当前事件, 接着事件就会交给这个ViewGroup处理, 即它的onTouchEvent方法就会被调用;如果这个ViewGroup的onInterceptTouchEvent方法返回false就表示它不拦截当前事件, 这时当前事件就会继续传递给它的子元素 接着子元素dispatchTouchEvent方法就会被调用 如此反复直到事件被最终处理。

三、事件分发的具体流程

事件分发的具体流程主要还是围绕上文所述的三个方法展开,当一个事件产生后,它的传递按如下顺序进行:Activity -> Window -> 顶级View(DecorView)-> 子View,事件总是先传递给Activity,Activity再次传递给Window,最后Window传递给顶级View(DecorView),顶级View接收到事件后会按照事件分发机制逐渐分发给下面的各个子View,整个过程大致可以用以下图来表示:


总结

1、Android 事件传递顺序为 Activity => ViewGroup => View

2、方法执行优先级从高到低onTouch()-->onTouchEvent() --> onClick(),只要优先级高的事件方法返回true,事件便就地结束,不再向下一层下发,排在后面的点击事件也就不会再被调用和响应了;

3、关于事件序列:一个事件序列是指从手指接触到屏幕的那一刻起,到手指离开的屏幕的那一瞬间结束,在这个过程中所产生的一系列的事件,这个事件以down事件开始,中间含有数量不等的move事件,最终以up事件结束,正常情况下,一个事件序列只能被一个View拦截且消耗,某个View一旦决定拦截,那么这整个事件序列都只能由该View处理。

4、View的onTouchEvent默认会消耗事件,除非他是不可点击的(clickbale和longClickable同时为false)

本文主要介绍了View事件分发的相关知识,如有错误还请各位大神指出,本文部分图示直接引用自网络,如有侵权请联系删除,谢谢!同时也欢迎各位大佬来讨论和交流技术,本人邮箱hbutys@vip.qq.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值