文章目录
目录
一、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事件种类有以下几种:
- MotionEvent.ACTION_DOWN 手指按下View
- MotionEvent.ACTION_MOVE 手指在View上移动
- MotionEvent.ACTION_UP 手指从View上抬起
- 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