Android 事件分发机制
概述
这里的事件指的是 View 对手势的响应,这里的手势包含四种
事件 | 说明 |
---|---|
ACTION_DOWN | 手指初次触摸屏幕时触发 |
ACTION_MOVE | 手指在屏幕滑动时触发,会多次触发 |
ACTION_UP | 手指离开屏幕时触发 |
ACTION_CANCEL | 事件被上层拦截时触发 |
一次 ACTION_DOWN --> ACTION_UP 流程称为一次事件
一次事件的消费 View 只有一个
事件分发过程的主要方法:
dispatchTouchEvent ()
onInterceptTouchEvent ()
onTouchEvent ()
View
分别用 true、false 来表示是否对当前事件进行分发成功、拦截、消费操作,而是否分发成功取决事件是否被消费,是否被消费取决于事件是否被拦截,所以说 dispatchTouchEvent 的执行结果取决于 onInterceptTouchEvent 和 onTouchEvent 的共同作用
所以事件的分发过程中,以上方法的执行顺序(当前 ViewGroup)dispatchTouchEvent --> onInterceptTouchEvent --> onTouchEvent
一个事件的具体传递顺序,我们通过 log 来展示:
以下为 Activity 嵌套 ViewGroup 嵌套 View 的组合
以上为经典的事件不消费流程:
- Activity 调用 ViewGroup 的 dispatchTouchEvent,ViewGroup 开始对事件进行处理
- 本身的 onInterceptTouchEvent 开始执行,看本身是否需要进行拦截,返回结果不拦截
- 那么调用子 View 的 dispatchTouchEvent,View 的 dispatchTouchEvent 开始执行
- 子 View 没有拦截事件,直接进入 onTouchEvent 开始执行
- onTouchEvent 不消费
- 子 View 的 dispatchTouchEvent 方法执行完毕,分发不成功返回 false
- ViewGroup 的 dispatchTouchEvent 方法执行完毕,分发不成功返回 false
log展示经典事件消费流程,这里以为了方便理解只展示 ACTION_DOWN 事件的消费流程
ACTION_UP 会一模一样的再执行一遍,一次 ACTION_DOWN --> ACTION_UP 流程称为一次事件
在以上的流程中,我们只需要重点知道事件是怎么处理的(View)以及事件是怎么分发的就可以了(ViewGroup)
View 事件的分发 dispatchTouchEvent
一个事件,首先会进行一系列相关判断,我们这里只讲一下有关判断
// View 中的 dispatchTouchEvent 主要做的是事件交给哪个方法处理(onClick、onTouch)
public boolean dispatchTouchEvent(MotionEvent event) {
ListenerInfo li = mListenerInfo;
if (
// mListenerInfo: 只要设置了类似于 onClickListener、onTouchListener等类似监听事件则都不为空
li != null
// 是否设置了触摸监听事件
&& li.mOnTouchListener != null
// view 是否可点击
&& (mViewFlags & ENABLED_MASK) == ENABLED
// 触摸监听是否返回的 true,也就是说事件是否被触摸时候消费了,这里也是 onTouch 方法的调用的地方
&& li.mOnTouchListener.onTouch(this, event)) {
// 如果以上条件都符合,则标记为 true
result = true;
// 综上,以上判断条件的关键判断为设置 onTouchListener 监听后 onTouch 方法的返回结果
}
// onTouchEvent 的执行位置在这个判断中
// onTouch 的返回结果 决定了 onTouchEvent 是否执行
// 也就是说事件在 onTouch 中被消费了,就不会在 onTouchEvent 中再次消费了
// 也说明了事件只能有一处消费,不仅仅是在不同 View 中,同一个 View 中也是
if (!result && onTouchEvent(event)) {
result = true;
}
// 实际的运行效果,onTouchListener 返回 true,onClickListener 不执行
// 因为 onClick 方法在 onTouchEvent 中被调用
}
View 事件的处理(消费)onTouchEvent:
在 dispatchTouchEvent 中调用,不一定会被调用
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_MOVE:
break;
}
ViewGroup
ViewGroup 事件的分发 dispatchTouchEvent
ViewGroup 中的 dispatchTouchEvent 主要做的是,事件分发给谁去处理(本身、子 View)
在执行过程中,主要有三个大的流程
- 判断是否拦截事件
- 将事件分发给子 View
- 处理事件
而 1 的结果决定 2 是否执行
// ViewGroup 的 dispatchTouchEvent
// 如果事件被当前 View 拦截了,代码的执行
public boolean dispatchTouchEvent(MotionEvent ev) {
// 第一个流程,判断是否拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// onInterceptTouchEvent 的调用的地方
// 主要代码,执行拦截方法,获取是否要拦截,此返回值决定了第二部分代码是否执行
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
// 第二个流程
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// ACTION_MOVE 时不执行
}
}
// 第三个流程
if (mFirstTouchTarget == null) {
// 自己本身去处理这个事件
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
// 最终调用 super.dispatchTouchEvent() 这里的 super 指的是父类,而不是当前的父 ViewGroup
} else {
}
}
// ---------------------------------------------------------------------------
// 如果事件当前 View 不拦截,代码的执行
public boolean dispatchTouchEvent(MotionEvent ev) {
// 第一个流程,判断是否拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// onInterceptTouchEvent 的调用的地方
// 主要代码,执行拦截方法,获取是否要拦截,此返回值决定了第二部分代码是否执行
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
// 第二个流程
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// ACTION_MOVE 时不执行
}
...
//
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// 询问当前 View 是否消费,如果不消费则继续寻找下一个
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
}
}
// 第三个流程
if (mFirstTouchTarget == null) {
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
// 多指操作处理
while (target != null) {
final TouchTarget next = target.next;
//
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
}