
今日分享
开发必备,面试必备。
介绍Android事件分发机制。
事件的介绍
- 何时产生
当用户触摸view
或者viewGroup
其派生对象的时候,会触发事件。 - 事件类型
主要谈论常用的三个事件类型(其他的暂不考虑在内)。
事件类型 | 内容 |
---|---|
ACTION_DOWN |
用户手指按下时进行触发 |
ACTION_MOVE |
手指按下到手指松开之间,如果移动距离大于一个数值就触发。 |
ACTION_UP |
手指离开屏幕触发 |
更加形象点的话就看如下事件示意图:

- 主要触发方法
在传递事件的过程中,主要涉及如下三个阶段:
1.分发(dispatch):
该阶段对应dispatchTouchEvent()
方法。
public boolean dispatchTouchEvent(MotionEvent event)
该方法中可以来控制事件是否继续分发到子视图处理。如果返回false,继续传递;否则表示消费掉了,不再传递了。还可以设置:
return super.dispatchTouchEvent(event);
表示继续分发事件。如果是ViewGroup
其中会有onInterceptTouchEvent()
方法,是否拦截当前事件。
- 拦截(Intercept)
该阶段对应onInterceptTouchEvent()
方法。
public boolean onInterceptTouchEvent(MotionEvent ev)
该方法的可以控制事件是否拦截,返回值如果是false或者super.onInterceptTouchEvent
方法表示不拦截,继续传递子视图。
- 消费(consume):
该阶段对应onTouchEvent
方法。
public boolean onTouchEvent(MotionEvent event)
该方法主要对应该视图处理该事件,返回值true表示事件就这样处理完了,不再传递给上层视图;否则表示当前视图没有处理完,还需要继续传递给上层视图。
- 会传递的类
在Android中,主要还是有三种类可以传递事件:
类 | 能力 |
---|---|
Activity |
分发和消费 |
View |
分发和消费 |
ViewGroup |
分发、拦截和消费 |
那肯定是activity
->viewGroup
->view
注意:
viewGroup
集成自View
,本文所表达的view
是那些集成自view
的空间,比如说TextView
,ImageView
等。
类型 | 介绍 |
---|---|
Activity |
四大组件之一 |
View |
所有控件基类 |
ViewGroup |
多种View的集合 |
简单都说就是从外部开始传递。
Activity事件分发分析
- 查看
dispatchTouchEvent
方法:
public boolean dispatchTouchEvent(MotionEvent ev) {
//ACTION_DOWN事件开始
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//从window对象开始分发
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//Window分发返回true表面之前有处理掉该事件了,就不会调用activity的ontTouchEvent。
return onTouchEvent(ev);
}
- 查看
onUserInteraction
方法
//按下功能键的时候回触发该方法(back,home等)
public void onUserInteraction() {
}
- 查看`onTouchEvent`分发:
public boolean onTouchEvent(MotionEvent event) {
//主要是对于处理边界外点击事件:
//是否是DOWN事件
//是否event的坐标在边界内
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
- 查看
getWindow().superDispatchTouchEvent()
:
public abstract boolean superDispatchTouchEvent(MotionEvent event);//一个抽象方法啊
在Window对象中只有该抽象方法,那它的实例方法在哪里呢?那就是要找window的子类(PhoneWindow)
- 查看PhoneWindow的superDispatchTouchEvent方法:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用的是mDcor(DecorView)
return mDecor.superDispatchTouchEvent(event);
}
- 查看DecorView的superDispatchTouchEvent方法:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);//viewGroup分发
}
终于看到了dispatchTouchEvent
方法(用来分发事件)。
DecorView
是什么呢?
窗口最顶层的视图
换句话说:DecorView
集成FrameLayout,
是所有界面的父界面。
在activity
界面样式外部包裹的就是DecorView
,这里调用的super.dispatchTouchEvent
方法开始就是FrameLayout
的dispatchTouchEvent
,就是ViewGroup
的dispatchTouchEvent
。
接下来的分析将结合实例结果,不再参考源码,5.0版本以上的分发源码还是很复杂的。如果想看源码解析的小伙伴可以阅读这篇机制详解
(https://www.jianshu.com/p/38015afcdb58)
实例分析
下面将进行测试,布局是在activity
中布局为EventRelativeLayout
,其中放有一个EventTextView
。点击EventTextView
,查看传递结果。
效果如图:

1)测试1
中间不做任何处理,返回值都是父类该方法。
看如下结果:

先调用
activity
的分发方法,到layout
的分发方法,再到layout
的拦截方法,不进行拦截,到了最后view
的分发方法,将调用本身消费方法,传递给上一级调用其消费方法,直到到达acitivity
的分发方法中的消费方法。不进行其他处理返回值都是
false
。那如果进行其他处理呢?
2)测试2
textView
的消费方法返回true,表示已经消费。看到如下结果:

TextView
消费掉了事件,事件不再继续传递给上层。3)测试3
Activity
分发事件返回true
,表示分发已经处理。
Activity
分发事件返回false
,表示事件未消费完。
发现
activity
不管返回false
还是true
,都不继续分发了,为什么呢?实际上后面的分发都是通过调用父类分发方法来查找下一个分发目标的。
4)测试4
Layout
分发不调用父类分发方法,返回true或者返回false:返回
true
:
事件被处理,不再继续传递。
返回
false
:
事件不再向下分发,将会调用上层
onTouchEvent
时间。5)测试5
Layout
拦截不调用父类分发,返回true或者返回false:返回
true
:
返回
false
:
表明拦截时间会拦截下层分发,然后会向上调用
onTouchEvent
分发消费掉。6)测试6
TextView
分发不调用父类分发,返回true
或者false
:返回true:

事件不再传递,消费掉了。
返回
false
:
事件不再向下层分发,而是向上调用消费方法。
测试完成,可以总结为如下图:
该图来自图解事件分发机制(https://www.jianshu.com/p/7cf6eb4ce7a9);

有兴趣的小伙伴可以结合源码,那样可以更加深刻的掌握事件分发机制。
可以阅读5.0版本以下的三个阶段的源码,那会简单些。
感谢阅读!
献上测试源码:https://github.com/xiaogoudandan/WilliamApp
献上二维码:

网友评论