场景:
手指点击屏幕之后,滑动一段距离然后抬起
事件:
ACTION_DOWN、ACTION_MOVE、…、ACTION_MOVE、ACTION_UP
先说结论:
- 如果子 view 未消费上次事件,那么后续事件不会再传递给它(也不再调用 onInterceptTouchEvent 方法)
- 如果子 view 消费了上次的事件,那么后续事件都会尝试着传递给它;后续事件会调用 onInterceptTouchEvent 方法
(之所以说尝试是因为 onInterceptTouchEvent 方法仍然可以拦截)- dispatchTouchEvent 所有、每次事件发生都会走
- 对于onInterceptTouchEvent,down 事件或上次事件被子 view 消费时才会走
伪代码
如下(对应源码:android.view.ViewGroup#dispatchTouchEvent()
):
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Check for interception.
final boolean intercepted;
//idea:意为只有本次为 DOWN 事件,或者上次的事件被子 view 消费时,才会再看看是否需要调用(未修改 mGroupFlags 时) onInterceptTouchEvent
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
//idea: 使用 requestDisallowInterceptTouchEvent 方法来处理滑动冲突就是利用了此行代码
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
//idea: restore action in case it was changed,也就是防止篡改
ev.setAction(action);
} 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;
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
//idea: 未取消掉且未被拦截时
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
for (int i = childrenCount - 1; i >= 0; i--) {
final View child = ...;
//idea 将其传递给子 view
if (child.dispatchTouchEvent(ev)) {
//idea 子 view 说要处理此事件时,获取 child 对应的 TouchTarget
final TouchTarget tmpTarget = TouchTarget.obtain(child, pointerIdBits);
tmpTarget.next = mFirstTouchTarget;
//idea: 重点出现了
mFirstTouchTarget = tmpTarget;
break;
}
}
}
}
}
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
}
// Update list of touch targets for pointer up or cancel, if needed.