ViewGroup事件分发机制

mac2024-03-09  40

事件分发机制3连

Activity的事件分发机制(https://blog.csdn.net/u013728021/article/details/102826314) ViewGroup事件分发机制(https://blog.csdn.net/u013728021/article/details/102831095) View的事件分发机制(https://blog.csdn.net/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 方法
最新回复(0)