好早之前简单看了ViewGroup事件分发,写的流水账:
https://blog.csdn.net/starry_eve/article/details/41483131#
现在发现没什么卵用,早就忘了,重新梳理了一下,有了新的理解,先复制代码:
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
return true;
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
}
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
}
mMotionTarget = null;
return true;
}
if (isUpOrCancel) {
mMotionTarget = null;
}
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
return target.dispatchTouchEvent(ev);
}
假定一个布局的所有节点都是ViewGroup。
事件流开始于在onTouchEvent处理ACTION_DOWN事件。
最开始,布局树状结构从顶层调用dispatchTouchEvent,递归传递到底层视图,底层视图onTouchEvent处理ACTION_DOWN优先结算。
onTouchEvent处理ACTION_DOWN导致dispatchTouchEvent返回true,进而使其parent的mMotionTarget指向之。
递归完毕后mMotionTarget形成链,由顶层可一直寻找到最底层的View。
拦截ACTION_DOWN:
拦截者把ACTION_CANCEL事件分发给自己,其底层无法感知ACTION_DOWN。
拦截其他事件:
拦截其他事件是以mMotionTarget为基础的,一个可以拦截的ViewGroup,其mMotionTarget必然不为空。
拦截之后,mMotionTarget链断裂,从顶部只能寻找到拦截的位置。被拦截者收到ACTION_CANCEL,并把其传递给底层。拦截者开始处理后续事件。
考察一个人对ViewGroup的理解是否深刻,可以出的问题:
1.父View拦截了我的ACTION_MOVE,我如何感知
2.父View拦截了我的ACTION_DOWN,我如何感知
3.我的onTouchEvent方法没使ACTION_DOWN返回true,父View是否能拦截我