- 本文分析从Activity是如何向下分发的,暂不分析事件从InputManagerService如何传递到Activity
- 下列均属于个人理解 若有不正确欢迎指正
- 下面分析基于Android11 源码
事件分发中的事件指的是什么
- 点击事件(Touch事件)即用户点击屏幕所产生的事件
- 该事件会被封装成MotionEvent对象
事件有哪几种类型
| 事件类型 | 具体触发动作 | 对应值 |
|---|---|---|
| MotionEvent.ACTION_DOWN | 用户按下发生动作 | 0 |
| MotionEvent.ACTION_UP | 用户抬起时 | 1 |
| MotionEvent.ACTION_MOVE | 用户滑动屏幕时 | 2 |
| MotionEvent.ACTION_CANCEL | 事件被中止取消 | 3 |
事件流指的是什么
- 上述事件
down(按下)move(滑动)up(抬起)cancel(中止/取消)组成了事件流 - 事件从
按下开始 中间伴随着N次``滑动事件,以抬起,中止/取消结束
事件分发(传递)又是指什么
- 将点击事件传递到具体处理事件的view的过程
事件从哪里开始分发的呢
- 当用户点击屏幕时 由
InputManagerService收集事件并交由WindowManagerService分发到Activity处理的 - 当然我们不在这里分析这个过程主要分析到了Activity之后是如何分发的
1事件分发
1.1分发事件的组件
- 与Activity 界面构成有关
Activity由Activity,PhoneWindow,DecorView,ViewGroup,View组成所以事件应在其中分发 - 但是
PhoneWindow调用的是DecorView的superDispatchTouchEvent也就是调用的ViewGroup的DispatchTouchEvent - 所以我们一般说的事件分发者 为
Activity,ViewGroup,View基于分发也从这三个组件分析 - 下图为事件传递方法调用时序图

1.2 分发涉及到的关键方法
- 由上图可知 关键方法
| Activity | ViewGroup | View | |
|---|---|---|---|
| dispatchTouchEvent | 有 | 有 | 有 |
| onInterceptTouchEvent | 没有 | 有 | 没有 |
| onTouchEvent | 有 | 没有 | 有 |
-
dispatchTouchEvent
- 从上面的图种可以看出这是唯一 一个三个组件都存在的方法
- 该方法用来分发事件
- 若组件该方法返回
true则表示事件已经被消费事件终止停止分发
- 这里的消费若该组件存在子View则事件可能被子View,或自身消费,若不存在子View则被自身消费 - 若组件该方法返回
false则事件,表示该组件没有 消费/处理事件,并将事件处理结果(此时事件处理结果为false)返回给该组件的父级(即调用该组件dispatchTouchEvent方法的组件 )由父级看自身是否处理事件,若不处理仍返回false,并再次向父级的父级传递,若一直不处理则一直传递到Activity这就是事件处理规则 - 综上所述可得结论
- 事件传递是由最外层到最内层即
Activity-ViewGroup-View - 事件处理是从内层开始并逐级返回到外层 若中间不产生事件拦截则从最内层VIew开始处理若产生拦截则从拦截View开始
- 事件传递是由最外层到最内层即
- 若组件该方法返回
-
onInterceptTouchEvent
- 该方法只存在于ViewGroup中 用来拦截事件
- 该方法返回
true表示事件被拦截要看自身是否处理事件 - 该方法返回
false表示事件不拦截,继续分发
-
onTouchEvent
- 对于这个只存在于Activity和View中的方法是用来处理事件的
- 该方法返回
true表示事件被消耗 - 该方法返回
false表示事件未消耗
-
下面我们看具体的源码来分析
2 事件在Activity中的分发
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// ->> 1 此处一般用于做屏保
onUserInteraction();
}
// ->> 2 若getWindow().superDispatchTouchEvent(ev)返回true 则直接返回true
// 说明事件被处理,事件停止分发 否则返回false 继续向下调用 onTouchEvent
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//->> 3 若getWindow().superDispatchTouchEvent(ev)返回false 则执行onTouchEvent
return onTouchEvent(ev);
}
- 从上面代码中可以看出,当事件传递到了Activity后首先调用了
getWindow().superDispatchTouchEvent(ev)该方法最终调用的是ViewGroup的dispatchTouchEvent - 即 事件先由Activity传递到
ViewGroup的dispatchTouchEvent- 若
ViewGroup的dispatchTouchEvent返回True- 则事件已经被
ViewGroup或其子View消费Activity的dispatchTouchEvent直接返回true 即事件消费停止分发
- 则事件已经被
- 若
ViewGroup的dispatchTouchEvent返回False- 则事件没有被消费 继续调用
Activity的onTouchEvent onTouchEvent返回true事件已消费则Activity的dispatchTouchEvent返回True事件消费让调用Activity的dispatchTouchEvent的组件知道该事件已经消费onTouchEvent返回False事件未消费则Activity的dispatchTouchEvent返回False事件未消费,让调用Activity的dispatchTouchEvent的组件知道该事件未消费,看否继续处理
- 则事件没有被消费 继续调用
- 若
3 ViewGroup中的事件分发
- 上面说到 事件到了Activity 后交给ViewGroup的
dispatchTouchEvent处理 下面看着部分源码
public boolean dispatchTouchEvent(MotionEvent ev) {
//......
boolean handled = false;
final boolean intercepted;
//....
//->> 1 得到 intercepted的值
intercepted = onInterceptTouchEvent(ev);
//....
//->> 2 检测是否拦截
if (!canceled && !intercepted) {
//....
//->> 3 遍历子View
for (int i = childrenCount - 1; i >= 0; i--) {
//...
// ->> 4 将事件分发至子view看子是否处理事件
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)){
//...
//->> 5 mFirstTouchTarget 赋值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
//->> 6 若有事件处理则跳出
break;
}
}
}
//....
//->> 7 看自身是处理事件
if (mFirstTouchTarget == null) {
//->> 8 向下分发事件 此时child为null 看自身是否处理事件
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}else {
//->> 9
handled = true;
}
//....
return handled;
}
// 事件向下分发 看是否自身处理事件 child==null 表示自身处理 child!=null 表示child处理
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
//--> 9 调用super的dispatchTouchEvent 也就是View的dispatchTouchEvent
handled = super.dispatchTouchEvent(event);
} else {
//->> 10 调用child的dispatchTouchEvent 若child是View 则调用View的dispatchTouchEvent
//若child是viewGroup 则调用ViewGroup的dispatchTouchEvent
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
return handled;
}
- 若
1处代码onInterceptTouchEvent返回true 拦截事件则2处检测为拦截if判断不会进入则不会执行3,4,5, 6即(不会执行遍历 子view ,询问子view 是否处理事件的过程)- 此时直接到了
7判断mFirstTouchTarget毫无疑问 遍历过程都没执行(3,4,5,6)所以该值一定为null此时执行8调用dispatchTransformedTouchEvent询问自身是否处理事件 其中参数child=null - 接着执行
9调用super.dispatchTouchEvent(event)ViewGroup继承自View即调用的是View的dispatchTouchEvent- 若该方法返回true 则表示事件已经消费 此时
Activity的dispatchTouchEvent返回true 事件消费 - 若该方法返回false 则表示事件已经未消费 此时
Activity执行onTouchEvent
- 若该方法返回true 则表示事件已经消费 此时
- 此时直接到了
- 若
1处代码返回的是false表示不拦截- 则
2处代码进入if判断 执行3 遍历子view - 执行
4 调用dispatchTransformedTouchEvent 询问子view是否处理事件此时child ! = null, - 此时执行
10调用child.dispatchTouchEvent- 若
child是ViewGroup则调用ViewGroup的dispatchTouchEvent所以这里有个递归一直递归到最内层View到最内层View的时候处理步骤就于child是View的处理步骤相同了 - 若
child是View则调用View的dispatchTouchEvent- 若返回true 则表示事件被消费 执行
5 mFirstTouchTarget 赋值- 执行
6 break 跳出循环 - 接着执行
7 判断 mFirstTouchTarget此时该值不为 null 执行9 handled = true;执行return该方法返回true事件消费,此时Activity的dispatchTouchEvent返回true 事件消费
- 执行
- 若返回
false- 则不执行
5 mFirstTouchTarget 赋值6 break 跳出循环 - 此时直接到了
7判断mFirstTouchTarget遍历过程虽然执行但是赋值 所以该值为null此时执行8调用dispatchTransformedTouchEvent询问自身是否处理事件 其中参数child=null - 接着执行
9调用super.dispatchTouchEvent(event)ViewGroup继承自View即调用的是View的dispatchTouchEvent- 若该方法返回true 则表示事件已经消费 此时
Activity的dispatchTouchEvent返回true 事件消费 - 若该方法返回false 则表示事件已经未消费 此时
Activity执行onTouchEvent - 从步骤
7开始 这里与onInterceptTouchEvent返回true 拦截事件中处理步骤就相同了
- 若该方法返回true 则表示事件已经消费 此时
- 则不执行
- 若返回true 则表示事件被消费 执行
- 若
- 则
4 View中的事件处理
- 根据上述
VIewGroup的事件分发最终是触发了View的dispatchTouchEvent看下其中部分源码 - 有意思的来了 这里为什么说的是事件处理 没说分发这个后面会说到
- 上源码
public boolean dispatchTouchEvent(MotionEvent event) {
//...
if (onFilterTouchEventForSecurity(event)) {
//...
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
//->> 1 看onTouchlistener是否未空,是否处理 返回true 处理 直接执行最后return
// 返回false 未处理执行 2 onTouchEvent
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//->> 2
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}
public boolean onTouchEvent(MotionEvent event) {
//....
//->> 3 判断是否可点击 若不可点击 直接返回 false 可点击则执行4 处理clickListener
// 设置则判断是否up 事件 是则处理click
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
//->> 4 up事件这里处理了ClickListener
performClickInternal();
break;
}
return true;
}
return false;
}
private boolean performClickInternal() {
return performClick();
}
public boolean performClick() {
// ->> 5 点击事件不为空则返回true 否则返回false
if (li != null && li.mOnClickListener != null) {
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
return result;
}
- 根据上述代码 事件传递到view中后我们意外的发现view中只有处理事件的代码并没有继续向下分发
- 那我们看下事件是如何处理
- 首先在代码
1看onTouchlistener是否未null若为null 则向下执行2 onTouchEvent若不为null看TouchListener是否处理事件- 若处理则返回
true,标记事件已经消费 不在向下执行,此时ViewGroup的dispatchTouchEvent返回trueActivity的dispatchTouchEvent返回true - 若不处理则返回
false事件未消费则向下执行2 onTouchEvent此时看代码3 判断view是否可点击- 若可点击返回
true则表示事件处理并执行代码4,5去处理ClickListener,此时ViewGroup的dispatchTouchEvent返回trueActivity的dispatchTouchEvent返回true - 若不可点击直接返回false 此时
ViewGroup的dispatchTouchEvent返回falseActivity执行onTouchEvent
- 若可点击返回
- 若处理则返回
- 接上面问题 我们发现View中其并没有做事件分发只是做了事件处理,ViewGroup则重写了View的dispatchTouchEvent 增加了一个事件拦截,和将事件传递到子View
- 所以我们理解是 VIewGroup存在事件分发 view 是处理事件
5 总结
- 根据上述图个人理解
+ 事件传递是由最外层到最内层即Activity-ViewGroup-View
+ 事件处理是从内层开始并逐级返回到外层 若中间不产生事件拦截则从最内层VIew开始处理若产生拦截则从拦截View开始
+ 可以理解成事件分发从外到内事件处理从内到外。。。
6 下图即为整个事件分发处理以及处理返回的过程

本文详细解析了Android中事件分发机制,包括点击事件的封装、事件类型、事件流概念及Activity、ViewGroup、View之间的事件传递过程。通过源码分析了事件如何从Activity分发至View,并最终被处理。
1933

被折叠的 条评论
为什么被折叠?



