曾几何时,当我还是一个 Android 小菜鸡的时候,我就被问过事件分发机制,当时我都是百度来的博客看看,回答的很浅显,当时是记住了过一段时间就忘。现在写一篇博客记录一下,下次在被问到就不怕了 (得瑟.gif)。一般情况下理解了以下几个问题,就基本掌握了事件分发的总体面貌:
一。为什么要理解事件分发?
答:1. 为了解决事件冲突(事件冲突就是:事件只有一个,多个控件想要处理且处理的控件不是我们想要给的控件就发生了冲突)
2. 为了不被面试官问的一脸懵逼( 懵逼警告⚠️ )
二。事件是什么?
简单的来说:事件(MotionEvent)就是手指触摸(Touch)手机屏幕而产生的,具体的产生过程后面有机会再分析。本文主要分析事件(MotionEvent)分发流程,记住下表尤其是 ACTION_MOVE 会多次触发,这个要记住了,后面分析源码经常会忘了这一点。
三。事件分发过程主要有哪些方法以及作用?
答:dispatchTouchEvent () 、onInterceptTouchEvent () 和 onTouchEvent ()
四。事件分发中分发的流程是怎么样的?(发车了,坐稳)
所以如果我们没有对控件里面的方法进行重写或更改返回值,而直接用 super 调用父类的默认实现,那么整个事件流向应该是从 Activity---->ViewGroup—>View 从上往下调用 dispatchTouchEvent 方法,一直到叶子节点(View)的时候,再由 View—>ViewGroup—>Activity 从下往上调用 onTouchEvent 方法。所以整个来说应该是一个 U 型的递归调用。
带大家看一下源码,还原一下 U 型调用,最好自己去看一遍源码,加深印象(我分析的是 Android29 版本的源码,各个版本可能会有点不一样,原理都一样)
4.1 Activity->ViewGroup 的事件传递机制
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
| public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
public abstract boolean superDispatchTouchEvent(MotionEvent event);
@Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); }
public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); }
|
4.2 ViewGroup 事件的分发机制
从上面分析可知,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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| @Override public boolean dispatchTouchEvent(MotionEvent ev) { ... if (onFilterTouchEventForSecurity(ev)) { final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); } else { intercepted = false; } } else { intercepted = true; } if (!canceled && !intercepted) { for (int i = childrenCount - 1; i >= 0; i--) { if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } } } if (mFirstTouchTarget == null) { handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS); } else { } } }
public boolean onInterceptTouchEvent(MotionEvent ev) { return false; }
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { if (child == null) { handled = super.dispatchTouchEvent(transformedEvent); } else { final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; transformedEvent.offsetLocation(offsetX, offsetY); if (! child.hasIdentityMatrix()) { transformedEvent.transform(child.getInverseMatrix()); } handled = child.dispatchTouchEvent(transformedEvent); } transformedEvent.recycle(); return handled; }
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { if (disallowIntercept) { mGroupFlags |= FLAG_DISALLOW_INTERCEPT; } else { mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } if (mParent != null) { mParent.requestDisallowInterceptTouchEvent(disallowIntercept); } }
|
五。总结
- 结论:Android 事件分发总是先传递到 ViewGroup、再传递到 View
- 当点击了某个控件时
down–确定事件给谁
1. 先看 ViewGroup 是否拦截后自己处理(即不分发下去)
2. 分发下去:排序 遍历分发 领取事件的 View 处理事件
3. 没子 View 领取,再看下自己是否处理事件
move–处理事件
1. 先看 ViewGroup 是否拦截后自己处理(即不分发下去)(子 View 可以请求不拦截 Move 事件)
2. 分发下去:直接由 down 事件确定的 view 处理
View 的事件处理下一篇文章会讲解,如果觉得这篇文章有用,欢迎点赞加收藏✨。
相关参考文章链接:
郭婶的事件分发机制完全解析
Android 事件分发机制详解:史上最全面、最易懂
Android 事件分发 mFirstTouchTarget 的思考