核心就是 ViewGroup 的 dispatchTouchEvent
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| public boolean dispatchTouchEvent(MotionEvent event) { final boolean isIntercept;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { if (disallowIntercept) { isIntercept = false; } else { isIntercept = onInterceptTouchEvent(event); } } else { isIntercept = true; }
if (!isCanceled && !isIntercept) { if (actionMasked == MotionEvent.ACTION_DOWN || ...) {
for (int i = childrenCount - 1; i >=0; i--) { final View child = ...;
if (!child.canViewReceivePointerEvent() || !isTransformedTouchPointInView(event)) { continue; }
if (dispatchTransformedTouchEvent(event, .., child)) {
newTouchTarget = addTouchTarget(child, ..); alreadyDispatchedToNewTouchEventTarget = true; break; } } } }
if (mFirstTouchTarget == null) { handled = dispatchTransformedTouchEvent(event, canceled, child = null, .); } else { TouchTarget target = mFirstTouchTarget; while(target != null) { target = target.next; if (alreadyDispatchedToNewTouchEventTarget && target == newTouchTarget) { handled = true; } else { if (dispatchTransformedTouchEvent(event, target, ...)) { handled = true; } } } }
return handled; }
|
后续的 MOVE、UP 等事件的分发交给谁,取决于它们的起始事件 Down 是由谁捕获的。
什么时候会出发 CANCEL 事件?
当父视图的 onInterceptTouchEvent 先返回 false,然后在子 View 的 dispatchTouchEvent 中返回 true(表示子 View 捕获事件),关键步骤就是在接下来的 MOVE 的过程中,父视图的 onInterceptTouchEvent 又返回 true,intercepted 被重新置为 true,此时上述逻辑就会被触发,子控件就会收到 ACTION_CANCEL 的 touch 事件。
如何强制父布局不拦截点击事件?
使用 requestDisallowInterceptTouchEvent(boolean disallow)
方法。
dispatchTouchEvent
的简化版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public boolean dispatchTouchEvent(MotionEvent event) { final boolean isIntercept;
if (disallowIntercept) { isIntercept = false; } else { isIntercept = onInterceptTouchEvent(event); }
if (!isIntercept) { for (int i = childrenCount - 1; i >= 0; i++) { View child = ...; boolean consumed = dispatchTransformedTouchEvent(child); if (consumed) { newTouchTarget = addTouchTarget(newTouchTarget); alreadyDispatched = true; break; } } }
boolean handled; if (newTouchTarget == null) { handled = dispatchTransformedTouchEvent(null); } else { TouchTarget next = mFirstTouchTarget; while(next != null) { next = next.next; handled = dispatchTransformedTouchEvent(next); } } return handled; }
|