事件分发机制3连
Activity的事件分发机制(https://blog.youkuaiyun.com/u013728021/article/details/102826314)
ViewGroup事件分发机制(https://blog.youkuaiyun.com/u013728021/article/details/102831095)
View的事件分发机制(https://blog.youkuaiyun.com/u013728021/article/details/102826446)
小结
每次按下直到取消或者抬起,都会进行一次事件分发。事件分发是由Activity传递给Window,Window传递给DecorView实际就是ViewGroup的dispatchTouchEvent这个方法进行分发。
- 如果ViewGroup的dispatchTouchEvent方法回false,那么只会响应ACTION_DOWN的事件,其它事件不再响应。
- 如果ViewGroup的dispatchTouchEvent方法回true,后续的其它事件还能继续分发到该View,也就是能继续响应其它事件。
ViewGroup的dispatchTouchEvent源码
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
... ...
// 是否进行过操作的记录
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// ACTION_DOWN 事件 即初次按下
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 清空TouchTargets
cancelAndClearTouchTargets(ev);
// 重新设置Touch的状态
resetTouchState();
// 经过这两个方法 mFirstTouchTarget会被置为空
}
// 判断是否对事件进行拦截
final boolean intercepted;
//如果是按下事件 或者 没有子View在消费事件
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 子View是否充许事件拦截,默认为false 即调用requestDisallowIntercept()方法,设置true的话告诉父View不要对此次事件序列进行拦截
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//子View允许时间拦截,即disallowIntercept为false
if (!disallowIntercept) {
// 调用ViewGroup的onInterceptTouchEvent方法的拦截方式
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
//没有子View处理事件并且不是初始化的Down事件,就对事件进行拦截
intercepted = true;
}
// 检查事件有没有取消
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
//如果不拦截并且不是canceled
if (!canceled && !intercepted) {
//如果是一个down事件 或者 split(多点触控的参数)或者
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
// 循环子View
final View[] children = mChildren;
// 从最后一个到第一个开始循环
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// isTransformedTouchPointInView(x, y, child, null)判断事件是不是在这个子View上
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// 分发事件的重要方法dispatchTransformedTouchEvent
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// addTouchTarget这个方法给mFirstTouchTarget赋值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
}
}
// 如果没有子View消费掉事件,就自己消费掉
if (mFirstTouchTarget == null) {
// 这里最终执行View的事件分发
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// TouchTarget是手指和被摸到的子View的绑定,是一个链表结构
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;
}
}
}
return handled;
}
true:子View请求当前ViewGroup不要拦截此次事件序列
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
// We're already in this state, assume our ancestors are too
return;
}
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// Pass it up to our parent
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
处理事件分发的重要方法
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// 删掉了N行代码...
if (child == null) {
// 去调用自己父类View的事件分发
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
// 调用子View的事件分发
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
-
正常的一次分发:
ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepterTouchEvnet -> View.dispatchTouchEvent -> View.onTouchEvent -> ViewGroup.onTouchEvent -
ViewGroup拦截了事件
也就是ViewGroup.onIntercepterTouchEvent 返回ture:
ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepterTouchEvent -> ViewGroup.onTouchEvent -
ViewGroup.onTouchEvent返回true,其它地方返回false:
**ACTION_DOWN:**ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepterTouchEvnet -> View.dispatchTouchEvent -> View.onTouchEvent -> ViewGroup.onTouchEvent
**ACTION_MOVE:**ViewGroup.dispatchTouchEvent -> ViewGroup.onTouchEvent
**ACTION_UP:**ViewGroup.dispatchTouchEvent -> ViewGroup.onTouchEvent
MOVE和UP事件,因为mFirstTarget为空,onIntercepterTouchEvnet 这个方法执行不了,子类的事件执行不了。
分发事件总结
- 如果说子 View 没有一个地方返回 true ,只会进来一次只会响应 DOWN 事件,代表不需要消费该事件,如果你想响应 后续事件如move、up等,必须找个地方ture。
- 对于ViewGroup来讲,如果你想拦截子 View 的 Touch 事件,可以覆写 onInterceptTouchEvent ,在里面进行相应的逻辑处理,返回 true 表示拦截 , 然后执行该 ViewGroup 的 onTouchEvent 方法 , 如果子 View 没有消费 touch 事件也会调用该 ViewGroup 的 onTouchEvent 方法