Android事件分发机制二:viewGroup与view对事件的处理 (7)

viewGroup中的源码很多,但大体的逻辑也就这三大部分。理解好MotionEvent和TouchTarget的设计,那么理解viewGroup的事件分发源码也是手到擒来。上面的源码我省略了一些细节内容,下面附上完整的viewGroup分发代码。

ViewGroup.java api29 public boolean dispatchTouchEvent(MotionEvent ev) { // 一致性检验器,用于调试用途 if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } // 辅助功能,用于辅助有障碍人群使用; // 如果这个事件是辅助功能事件,那么他会带有一个target view,要求事件必须分发给该view // 如果setTargetAccessibilityFocus(false),表示取消辅助功能事件,按照常规的事件分发进行 // 这里表示如果当前是目标target view,则取消标志,直接按照普通分发即可 // 后面还有很多类似的代码,都是同样的道理 if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) { ev.setTargetAccessibilityFocus(false); } boolean handled = false; // 对遮盖状态进行过滤 if (onFilterTouchEventForSecurity(ev)) { // action的高9-16位表示索引值 // 低1-8位表示事件类型 // 只有down或者up事件才有索引值 final int action = ev.getAction(); // 获取到真正的事件类型 final int actionMasked = action & MotionEvent.ACTION_MASK; // ACTION_DOWN事件,表示这是一个全新的事件序列,会清除所有的touchTarget,重置所有状态 if (actionMasked == MotionEvent.ACTION_DOWN) { cancelAndClearTouchTargets(ev); resetTouchState(); } // 判断是否需要拦截 final boolean intercepted; // down事件或者有target的非down事件则需要判断是否需要拦截 // 否则直接拦截自己处理 if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { // 此标志为子view通过requestDisallowInterupt方法设置 // 禁止viewGroup拦截事件 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { // 调用onInterceptTouchEvent判断是否需要拦截 intercepted = onInterceptTouchEvent(ev); // 恢复事件状态 ev.setAction(action); } else { intercepted = false; } } else { // 自己消费了down事件 intercepted = true; } // 如果已经被拦截、或者已经有了目标view,取消辅助功能的target标志 if (intercepted || mFirstTouchTarget != null) { ev.setTargetAccessibilityFocus(false); } // 判断是否需要取消 // 这里有很多种情况需要发送取消事件 // 最常见的是viewGroup拦截了子view的ACTION_MOVE事件,导致事件序列中断 // 那么需要发送cancel事件告知该view,让该view做一些状态恢复工作 final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; // 三个变量: // 是否需要对事件进行分裂,对应多点触摸事件 // newTouchTarget 如果是down或pointer_down事件的新的绑定target // alreadyDispatchedToNewTouchTarget 是否已经分发给target view了 final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; // 下面部分的代码是寻找消费down事件的子控件 // 如果没有取消和拦截进入分发 if (!canceled && !intercepted) { // 如果是辅助功能事件,我们会寻找他的target view来接收这个事件 View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; // down或pointer_down事件,表示新的手指按下了,需要寻找接收事件的view if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { // 多点触控会有不同的索引,获取索引号 // 该索引位于MotionEvent中的一个数组,索引值就是数组下标值 // 只有up或down事件才会携带索引值 final int actionIndex = ev.getActionIndex(); // 这个整型变量记录了TouchTarget中view所对应的触控点id // 触控点id的范围是0-31,整型变量中哪一个二进制位为1,则对应绑定该id的触控点 // 例如 00000000 00000000 00000000 10001000 // 则表示绑定了id为3和id为7的两个触控点 // 这里根据是否需要分离,对触控点id进行记录, // 而如果不需要分离,则默认接收所有触控点的事件 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS; // 清除之前获取到该触控id的TouchTarget removePointersFromTouchTargets(idBitsToAssign); // 如果子控件的数量等于0,那么不需要进行遍历只能给viewGroup自己处理 final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { // 使用触控点索引获取触控点位置 final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); // 从前到后创建view列表 final ArrayList<View> preorderedList = buildTouchDispatchChildList(); // 这一句判断是否是自定义view顺序 final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; // 遍历所有子控件 for (int i = childrenCount - 1; i >= 0; i--) { // 获得真正的索引和子view final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); // 如果是辅助功能事件,则优先给对应的target先处理 // 如果该view不处理,再交给其他的view处理 if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } // 检查该子view是否可以接受触摸事件和是否在点击的范围内 if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } // 检查该子view是否在touchTarget链表中 newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // 链表中已经存在该子view,说明这是一个多点触摸事件 // 将新的触控点id绑定到该TouchTarget上 newTouchTarget.pointerIdBits |= idBitsToAssign; break; } // 设置取消标志 // 下一次再次调用这个方法就会返回true resetCancelNextUpFlag(child); // 找到合适的子view,把事件分发给他,看该子view是否消费了down事件 // 如果消费了,需要生成新的TouchTarget // 如果没有消费,说明子view不接受该down事件,继续循环寻找合适的子控件 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // 保存信息 mLastTouchDownTime = ev.getDownTime(); if (preorderedList != null) { // childIndex points into presorted list, find original index for (int j = 0; j < childrenCount; j++) { if (children[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); // 保存该view到target链表 newTouchTarget = addTouchTarget(child, idBitsToAssign); // 标记已经分发给子view,退出循环 alreadyDispatchedToNewTouchTarget = true; break; } // 辅助功能事件对应的targetView没有消费该事件,则继续分发给普通view ev.setTargetAccessibilityFocus(false); }// 这里对应for (int i = childrenCount - 1; i >= 0; i--) if (preorderedList != null) preorderedList.clear(); }// 这里对应判断:(newTouchTarget == null && childrenCount != 0) if (newTouchTarget == null && mFirstTouchTarget != null) { // 没有子view接收down事件,直接选择链表尾的view作为target newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; } }// 这里对应if (actionMasked == MotionEvent.ACTION_DOWN...) }// 这里对应if (!canceled && !intercepted) if (mFirstTouchTarget == null) { // 经过了前面的处理,到这里touchTarget依旧为null,说明没有找到处理down事件的子控件 // 或者down事件被viewGroup本身消费了,所以该事件由viewGroup自己处理 // 这里调用了dispatchTransformedTouchEvent方法来分发事件 handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { // 已经有子view消费了down事件 TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; // 遍历所有的TouchTarget并把事件分发下去 while (target != null) { final TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { // 表示事件在前面已经处理了,不需要重复处理 handled = true; } else { // 正常分发事件或者分发取消事件 final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; // 这里调用了dispatchTransformedTouchEvent方法来分发事件 if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } // 如果发送了取消事件,则移除该target if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } // 如果接收到取消获取up事件,说明事件序列结束 // 直接删除所有的TouchTarget if (canceled || actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { // 清除记录的信息 resetTouchState(); } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { final int actionIndex = ev.getActionIndex(); final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); // 如果仅仅只是一个PONITER_UP // 清除对应触控点的触摸信息 removePointersFromTouchTargets(idBitsToRemove); } }// 这里对应if (onFilterTouchEventForSecurity(ev)) if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; } View对于事件的分发

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzxwf.html