ViewGroup # dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 如果检查对象不空就检查事件,在View中也有这一步,但是传入的嵌套级别为0。
// 注释中有说如果事件已经被嵌套级别较高的View检查过了就不会再检查一遍了。
// 所以这里对事件已经进行过1等级的处理了,分发给下级View时就不会重新检查了。
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
// 如果事件目标是焦点View,并且这个View就处于焦点或是焦点View的嵌套父View
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
// 设置不需要判断,继续正常分发工作,以后就不需要重复判断了。
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
// 如果设置了窗口遮挡时过滤,并且确实有窗口遮挡,就过滤。
// 返回false表示过滤,这个过滤是在构造器通过判断style来设置的。
// 如果不过滤,就进入下面的操作。
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
// 如果是ACTION_DOWN事件,说明是新一事件序列的开始。
// 进行一些初始化工作,清理之前的状态。
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
// private void cancelAndClearTouchTargets(MotionEvent event) {
// // 如果上个事件序列中消耗事件的View的引用还没来得及回收
// if (mFirstTouchTarget != null) {
// boolean syntheticEvent = false;
// // 如果事件为空,就创造一个ACTION_CANCEL事件
// // 从disptachTouchEvent()中调用的不会进入这个方法块,因为已经判断了为ACTION_DOWN
// if (event == null) {
// final long now = SystemClock.uptimeMillis();
// event = MotionEvent.obtain(now, now,
// MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
// event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
// syntheticEvent = true;
// }
// for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
// // 如果参数View设置了PFLAG_CANCEL_NEXT_UP_EVENT标识位,就取消
// // private static boolean resetCancelNextUpFlag(@NonNull View view) {
// // if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
// // view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
// // return true;
// // }
// // return false;
// // }
// resetCancelNextUpFlag(target.child);
// // 这里主要是想让所有链上的所有View都传递一个ACTION_CANCEL
// // private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
// // View child, int desiredPointerIdBits) {
// // final boolean handled;
// // final int oldAction = event.getAction();
// // // 需要取消或是事件类型为ACTION_CANCEL
// // if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
// // // 传参需要取消,不管event是什么类型,都先设置一个ACTION_CANCEL
// // event.setAction(MotionEvent.ACTION_CANCEL);
// // // 如果没有子View了,就交给View来处理
// // if (child == null) {
// // handled = super.dispatchTouchEvent(event);
// // } else {
// // // 如果有就传递给子View继续分发
// // handled = child.dispatchTouchEvent(event);
// // }
// // // 再设置回原来的样子
// // event.setAction(oldAction);
// // return handled;
// // }
// // // ......
// // }
// dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
// }
// // private void clearTouchTargets() {
// // TouchTarget target = mFirstTouchTarget;
// // if (target != null) {
// // // 回收链上的每一个TouchTarget
// // do {
// // TouchTarget next = target.next;
// // target.recycle();
// // target = next;
// // } while (target != null);
// // mFirstTouchTarget = null;
// // }
// // }
// clearTouchTargets();
// // 如果事件为空就回收事件
// if (syntheticEvent) {
// event.recycle();
// }
// }
//}
cancelAndClearTouchTargets(ev);
// private void resetTouchState() {
// 回收TouchTarget
// clearTouchTargets();
// 撤销PFLAG_CANCEL_NEXT_UP_EVENT
// resetCancelNextUpFlag(this);
// 撤销FLAG_DISALOW_INTERCEPT
// 所以这个不允许拦截的标志位对ACTION_DOWN事件无效
// mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
// mNestedScrollAxes = SCROLL_AXIS_NONE;
// }
resetTouchState();
}
// Check for interception.
// 检查是否拦截
final boolean intercepted;
// 如果是ACTION_DOWN或者事件序列的前几个事件已经有子View消耗了
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
// 没有设置不允许拦截才能去判断是否拦截
if (!disallowIntercept) {
// 调用onInterceptTouchEvent判断是否需要拦截
// 默认返回false,不拦截
intercepted = onInterceptTouchEvent(ev);
// 防止操作中将event的类型改变了
ev.setAction(action); // restore action in case it was changed
} else {
// 如果设置了,就不拦截
intercepted = false;
}
} else {
// 没有消耗而且也不是ACTION_DOWN事件,就拦截。
// 会有两种情况,
// 一种是一开始该ViewGroup就拦截了ACTION_DOWN事件
// 或者子View没有消耗前几个事件,它们对于之后的事件也不感兴趣,所以拦截
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
// If intercepted, start normal event dispatch. Also if there is already
// a view that is handling the gesture, do normal event dispatch.
// 如果拦截或这已经有View拦截了事件,就开始正常的分发工作
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
// 如果设置了FLAG_CANCEL_NEXT_UP_EVENT就取消这个设置
// 或者该事件是cancel事件
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
// 是否拆分事件,默认可拆分
// 注释说在必要的情况下,可以将一个事件分解传递给多个子View
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
// 如果事件没有取消且不拦截
if (!canceled && !intercepted) {
// ......
}
// Dispatch to touch targets.
// 如果事件序列中还没有分发消耗的事件对象
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
// 尝试自己消耗事件
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
// 遍历到消耗事件的子View了,置handled为true
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
// 如果设置了PFLAG_CANCEL_NEXT_UP_EVENT就取消 或 需要拦截 返回true
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
// 如果cancelChild为true,就让该child分发一个cancel事件
// 如果cancelChild为false,就让该child分发该事件
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;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// 如果是取消或up事件或鼠标移动事件,就清空所有状态
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
// 当有手指抬起时,清空这个手指之前触碰点的所有信息
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
// private void removePointersFromTouchTargets(int pointerIdBits) {
// TouchTarget predecessor = null;
// TouchTarget target = mFirstTouchTarget;
// while (target != null) {
// final TouchTarget next = target.next;
// if ((target.pointerIdBits & pointerIdBits) != 0) {
// target.pointerIdBits &= ~pointerIdBits;
// // 如果除了需要删除的值之外就没有别的了,就将这个结点删除并回收
// if (target.pointerIdBits == 0) {
// if (predecessor == null) {
// mFirstTouchTarget = next;
// } else {
// predecessor.next = next;
// }
// target.recycle();
// target = next;
// continue;
// }
// }
// predecessor = target;
// target = next;
// }
//}
removePointersFromTouchTargets(idBitsToRemove);
}
}
// 发起检查
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
非取消事件且不拦截的分发消耗过程
if (!canceled && !intercepted) {
// If the event is targeting accessiiblity focus we give it to the
// view that has accessibility focus and if it does not handle it
// we clear the flag and dispatch the event to all children as usual.
// We are looking up the accessibility focused host to avoid keeping
// state since these events are very rare.
// 如果该View设置了FLAG_TARGET_ACCESSIBILITY_FOCUS
// 就找到下一层正真处焦点的View
// private View findChildWithAccessibilityFocus() {
// ViewRootImpl viewRoot = getViewRootImpl();
// if (viewRoot == null) {
// return null;
// }
// View current = viewRoot.getAccessibilityFocusedHost();
// if (current == null) {
// return null;
// }
// ViewParent parent = current.getParent();
// // 一层一层向上找
// // 直到找到一个View为该ViewGroup的child并且还嵌套着焦点View
// while (parent instanceof View) {
// if (parent == this) {
// return current;
// }
// current = (View) parent;
// parent = current.getParent();
// }
// // 如果没有找到,说明该ViewGroup没有嵌套焦点View
// return null;
//}
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
// 如果是ACTION_DOWN
// 或可分离且非第一个手指按下的情况,比如第一个手指按下并没有松开,此时又一只手指按下
// 或者是鼠标移动且并未点击,4.0后会监听鼠标移动
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
// 如果可分离就通过索引获取的值左移一位
// 否则取常量 -1
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
// 清除这个id早前的作用
removePointersFromTouchTargets(idBitsToAssign);
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.
// public ArrayList<View> buildTouchDispatchChildList() {
// return buildOrderedChildList();
// }
// ArrayList<View> buildOrderedChildList() {
// final int childrenCount = mChildrenCount;
// // 如果子View数小于等于1
// // 或者遍历子view不存在子getZ()不为0的view,也就是没有都没有高度时
// // z属性用于描述视图距离它父视图的高度
// if (childrenCount <= 1 || !hasChildWithZ()) return null;
// if (mPreSortedChildren == null) {
// mPreSortedChildren = new ArrayList<>(childrenCount);
// } else {
// // callers should clear, so clear shouldn't be necessary, but for safety...
// mPreSortedChildren.clear();
// // 重新分配日大小
// mPreSortedChildren.ensureCapacity(childrenCount);
// }
// final boolean customOrder = isChildrenDrawingOrderEnabled();
// // 开始遍历子View
// for (int i = 0; i < childrenCount; i++) {
// // add next child (in child order) to end of list
// // 是否设置有FLAG_USE_CHILD_DRAWING_ORDER
// // 如果设置了,绘制的方法就会通过调用getChildDrawingOrder()来决定绘制子View的顺序。
// // private int getAndVerifyPreorderedIndex(int childrenCount, int i, boolean customOrder) {
// // final int childIndex;
// // // 如果需要根据getChildDrawingOrder()排序子view的绘制顺序,就需要获得绘制顺序
// // if (customOrder) {
// // // 得到第i个View的绘制顺序,如果没有重写getChildDrawingOrder(),就会直接返回i
// // // protected int getChildDrawingOrder(int childCount, int i) {
// // // return i;
// // // }
// // final int childIndex1 = getChildDrawingOrder(childrenCount, i);
// // // 如果大于总数了,就抛异常
// // if (childIndex1 >= childrenCount) {
// // throw new IndexOutOfBoundsException("getChildDrawingOrder() "
// // + "returned invalid index " + childIndex1
// // + " (child count is " + childrenCount + ")");
// // }
// // childIndex = childIndex1;
// // } else {
// // // 否则直接返回遍历的下标
// // childIndex = i;
// // }
// // return childIndex;
// // }
// final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
// // 获得这个View
// final View nextChild = mChildren[childIndex];
// final float currentZ = nextChild.getZ();
// // insert ahead of any Views with greater Z
// int insertIndex = i;
// // 向数组前面遍历查找是否有深度大于当前子View的
// while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
// insertIndex--;
// }
// // 将当前子View插入到这个位置,使集合按getZ()从小到大排序
// mPreSortedChildren.add(insertIndex, nextChild);
// }
// // 将排序好的集合返回
// return mPreSortedChildren;
// }
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
// true 完全根据排序方法的自定义实现,getZ()都不考虑
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// 从后往前
for (int i = childrenCount - 1; i >= 0; i--) {
// 获取设置了顺序绘制后对应第i个绘制的view的下标
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
// preorderedList不空就从它里面取出第childIndex个View
// 空的话从children中取
// 变量:几个child 是否设置了z 是否设置绘制顺序排序
// 0 小于两个 都没设置 没有
// 完全默认顺序
// 0 0 0 childIndex默认 children的第i个
// 0 1 0 childIndex默认 children的第i个
// 0 1 0 childIndex默认 children的第i个
// 1 0 0 childIndex默认 children的第i个
// 完全绘制排序
// 0 0 1 childIndex有顺序 children的第i对应的顺序个
// 0 1 1 childIndex有顺序 children的第childIndex个
// 1 0 1 childIndex有顺序 children的第childIndex个
// 第一排序为getZ()的升序 第二排序会绘制顺序
// 1 1 0 childIndex默认 preorderedList的第i个
// 1 1 1 childIndex默认 preorderedList的第i个
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.
// 该viewgroup设置事件了指向焦点View并且焦点View在前面已经找到了
if (childWithAccessibilityFocus != null) {
// 判断遍历的此View是否是焦点View,如果不是就直接下一遍循环
if (childWithAccessibilityFocus != child) {
continue;
}
// 如果是的话就将找到的焦点view置空
// i回到到数第一个下标
// 这样做的目的是先让该焦点view尝试进行下面的普通分发操作
// 如果成功了,会在下面跳出循环。
// 如果不成功,就将记录的焦点view置空,
// 从最后一个开始重新遍历,不再进入这个判断。
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
// private static boolean canViewReceivePointerEvents(@NonNull View child) {
// // 可见或者存在动画
// return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
// || child.getAnimation() != null;
// }
// isTransformedTouchPointInView()用来盘那段事件是否在view的范围内
// protected boolean isTransformedTouchPointInView(float x, float y, View child,
// PointF outLocalPoint) {
// final float[] point = getTempPoint();
// point[0] = x;
// point[1] = y;
// // 把原来相对于该ViewGroup的坐标转换为相对于正在遍历的这个View的相对坐标
// // public void transformPointToViewLocal(float[] point, View child) {
// // point[0] += mScrollX - child.mLeft;
// // point[1] += mScrollY - child.mTop;
// // if (!child.hasIdentityMatrix()) {
// // child.getInverseMatrix().mapPoints(point);
// // }
// // }
// transformPointToViewLocal(point, child);
// // /*package*/ final boolean pointInView(float localX, float localY) {
// // return pointInView(localX, localY, 0);
// // }
// // public boolean pointInView(float localX, float localY, float slop) {
// // return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
// // localY < ((mBottom - mTop) + slop);
// // }
// // 判断是否在view的范围内
// final boolean isInView = child.pointInView(point[0], point[1]);
// // 将转换后的坐标设置给outLocalPoint
// if (isInView && outLocalPoint != null) {
// outLocalPoint.set(point[0], point[1]);
// }
// // 返回是否在范围内
// return isInView;
// }
// 如果不可见并且没有动画 或者 事件不在该View范围内
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
// 设置false,直接跳出进入下一次循环
ev.setTargetAccessibilityFocus(false);
continue;
}
// 找到child是正在遍历的view的Touch Target
newTouchTarget = getTouchTarget(child);
// 如果存在,说明这个view之前处理过事件,再给它这个事件并跳出循环。
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
// 取消该view的PFLAG_CANCEL_NEXT_UP_EVENT
resetCancelNextUpFlag(child);
// 将事件传递给child看是否能分发消耗成功,如果成功就进方法块
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
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]) {
// 找到该view的原始下标
mLastTouchDownIndex = j;
break;
}
}
} else {
// 如果集合为空,也就说明child是根据原始下标取出的,直接赋值
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
// 上面已经判断了Touch Target存在的情况
// 这里一定不存在,新创建一个对象并加入链表
// private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
// final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
// target.next = mFirstTouchTarget;
// mFirstTouchTarget = target;
// return target;
// }
// 头插,现在mFirstTouchTarget就指向这个view的touch target了
newTouchTarget = addTouchTarget(child, idBitsToAssign);
// 置为true表示已经分发给新目标了
alreadyDispatchedToNewTouchTarget = true;
// 跳出遍历子View的循环
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
// 焦点View没有成功处理事件所以将flag置为false,以后就普通的去遍历分发了
ev.setTargetAccessibilityFocus(false);
}
// 清空排序集合
if (preorderedList != null) preorderedList.clear();
}
// 只有当上面的if判断没有进入才会出现newTouchTarget为空的现象
// 就说明该viewgroup的子view数量为0
// 没有子view并且之前消耗过事件
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
// 找到消耗链表的最后一个元素,也就是第一个被添加的元素,down事件消耗的view
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
// 给这个目标加上这个事件的id
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
dispatchTouchEvent()框图
分发消耗框图
排序子View数组框图
网友评论