「这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战」
1. ViewGroup.dispatchTouchEvent()
- ViewGroup每次事件分发时,都需调用 onInterceptTouchEvent() 询问是否拦截事件
- disallowIntercept:是否禁用事件拦截的功能(默认是false),可通过调用requestDisallowInterceptTouchEvent()修改
- 通过for循环,遍历了当前ViewGroup下的所有子View
源码分析
这里主要是判断是否有拦截,调用 onInterceptTouchEvent(ev) 来判断是否有拦截。如果VieGroup的onInterceptTouchEvent()第一次执行为true,则mFirstTouchTarget = null,则也会使得接下来不会调用onInterceptTouchEvent(),直接将拦截设置为true
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
从父视图开始遍历
- (1)如果当前视图无法获取用户焦点,跳过本次循环
- (2)如果view不可见,或者触摸的坐标点不在view的范围内,跳过本次循环
- (3)寻找newTouchTarget,如果已经存在newTouchTarget,说明正在接收触摸事件,跳出循环
- (4)如果触摸位置在child的区域内,把事件分发给子View或ViewGroup
- (5)获取TouchDown的x,y坐标
- (6)dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign) 是调用子元素的dispatchTouchEvent()方法 , 传递的child不为空,所以就会调用子元素的dispatchTouchEvent()
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
// (1)
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
// (2)
if (!child.canReceivePointerEvents()|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// (3)
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
. . .
// (4)
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
// (5)
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
. . .
}
onInterceptTouchEvent()
- 返回false:不拦截(默认)
- 返回true:拦截,即事件停止往下传递(需复写onInterceptTouchEvent()其返回true)
- 在onInterceptTouchEvent返回TURE表示拦截时,实际调用的是super.dispatchTouchEvent方法,即View的该方法,进而由该方法调用onTouchEvent
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
2. 总结
- 当前ViewGroup拦截了事件链中的任何一次事件,那么该事件链后续的事件都会被拦截下来,即onInterceptTouchEvent() 返回false;并且后续的事件也不会再走onInterceptTouchEvent()
- 能接收事件的View要么是可见的,要么设置了动画
- 整个事件分发流程的主要就是在ViewGroup,因为View是不具备分发功能