文章目录
Android 事件分发 系列文章目录
前言
一、获取子组件
二、当前遍历的子组件的事件分发
三、ViewGroup 事件分发相关源码
前言
接上一篇博客 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 二 ) , 继续分析 ViewGroup 的事件分发机制后续代码 ;
一、获取子组件
之前已经按照 Z 轴深度 , 将组件进行排序 , 放在集合中 ;
倒序遍历排列好的组件 , 按照 Z 轴的上下顺序 , 先遍历的 Z 轴方向上 , 放在最上面的组件 , 也就是顶层组件 ;
for (int i = childrenCount - 1; i >= 0; i--) {
先获取组件索引 , 然后获取索引对应的子组件 ;
// 获取索引 final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); // 获取索引对应组件 final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex);
然后判定组件是否符合要求 :
调用 canViewReceivePointerEvents 方法 , 判定组件是否可见 , 会否处于动画中 ;
/** * Returns true if a child view can receive pointer events. * 判定控件是否可见 / 是否处于动画中 * @hide */ private static boolean canViewReceivePointerEvents(@NonNull View child) { return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null; }
调用 isTransformedTouchPointInView 判定手指是否在控件上面 ;
/** * Returns true if a child view contains the specified point when transformed * into its coordinate space. * Child must not be null. * 判定手指是否触摸到了组件 , 是否在组件区域范围内 * @hide */ protected boolean isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint) { // 获取当前坐标 final float[] point = getTempPoint(); point[0] = x; point[1] = y; transformPointToViewLocal(point, child); final boolean isInView = child.pointInView(point[0], point[1]); if (isInView && outLocalPoint != null) { outLocalPoint.set(point[0], point[1]); } return isInView; }
如果上面 3 33 个条件只要存在 1 11 个不满足 , 则不传递触摸事件 , 在遍历时直接 continue , 越过该控件 , 遍历下一个控件 ;
// X 控件范围 A , 如果手指按在 B 范围 , 不会触发 X 控件的事件 // 判定当前的组件是否可见 , 是否处于动画过程中 // ① canViewReceivePointerEvents 判定组件是否可见 , 会否处于动画 // ② isTransformedTouchPointInView 判定手指是否在控件上面 ; // 上述两种情况 , 不触发事件 if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); // 不触发事件 continue; }
ViewGroup | dispatchTouchEvent 方法相关源码 :
@UiThread public abstract class ViewGroup extends View implements ViewParent, ViewManager { @Override public boolean dispatchTouchEvent(MotionEvent ev) { ... // 倒序遍历 按照 Z 轴的上下顺序 , 排列好的组件 // 先遍历的 Z 轴方向上 , 放在最上面的组件 , 也就是顶层组件 for (int i = childrenCount - 1; i >= 0; i--) { // 获取索引 final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); // 获取索引对应组件 final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); // If there is a view that has accessibility focus we want it // to get the event first and if not handled we will perform a // normal dispatch. We may do a double iteration but this is // safer given the timeframe. // 无障碍 辅助功能 if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } // X 控件范围 A , 如果手指按在 B 范围 , 不会触发 X 控件的事件 // 判定当前的组件是否可见 , 是否处于动画过程中 // ① canViewReceivePointerEvents 判定组件是否可见 , 会否处于动画 // ② isTransformedTouchPointInView 判定手指是否在控件上面 ; // 上述两种情况 , 不触发事件 if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); // 不触发事件 continue; } // 截止到此处 , 可以获取子组件进行操作 ... } /** * Returns true if a child view can receive pointer events. * 判定控件是否可见 / 是否处于动画中 * @hide */ private static boolean canViewReceivePointerEvents(@NonNull View child) { return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null; } /** * Returns true if a child view contains the specified point when transformed * into its coordinate space. * Child must not be null. * 判定手指是否触摸到了组件 , 是否在组件区域范围内 * @hide */ protected boolean isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint) { // 获取当前坐标 final float[] point = getTempPoint(); point[0] = x; point[1] = y; transformPointToViewLocal(point, child); final boolean isInView = child.pointInView(point[0], point[1]); if (isInView && outLocalPoint != null) { outLocalPoint.set(point[0], point[1]); } return isInView; } }
源码路径 : /frameworks/base/core/java/android/view/ViewGroup.java