1. 如果一个布局有两个按钮重合了点击按钮为什么只有一个点击到,先点击到哪个?
之前分析过事件右Activity分发到ViewGroup再分发到view,那么看一下ViewGroup的分发方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//.....下面是主要代码
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.查找可以接受event的view
// Scan children from front to back. 扫描view从上到下
// 为view排序的方法接下来进入此方法看一下
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
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);
//.....
}
为view排序的集合
ArrayList<View> buildOrderedChildList() {
final int childrenCount = mChildrenCount;
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();
for (int i = 0; i < childrenCount; i++) {
// add next child (in child order) to end of list
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View nextChild = mChildren[childIndex];
final float currentZ = nextChild.getZ();
// insert ahead of any Views with greater Z
int insertIndex = i;
while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
insertIndex--;
}
mPreSortedChildren.add(insertIndex, nextChild);
}
return mPreSortedChildren;
}
经过排序,ViewGroup事件会先分配到上层view然后再一次传递直到事件被消费终止。
2. 什么情况view可以接收事件(什么情况view不会消费事件)
在ViewGroup的dispatchTouchEvent方法里面有这样一个判断
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
这三点必须都满足才当前view才不会被continue掉进行下一个循环
第一个!canViewReceivePointerEvents(child)。和VISIBLE状态和currentAnimation有关
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;
transformPointToViewLocal(point, child);
final boolean isInView = child.pointInView(point[0], point[1]);
if (isInView && outLocalPoint != null) {
outLocalPoint.set(point[0], point[1]);
}
return isInView;
}
第三个··········
/** @hide */
public final void setTargetAccessibilityFocus(boolean targetsFocus) {
final int flags = getFlags();
nativeSetFlags(mNativePtr, targetsFocus
? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS
: flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS);
}