文章目录
主要参考文献:任玉刚《Android开发艺术探索》
1. 基本介绍
1.1 MotionEvent分类
事件 | 简介 |
---|---|
ACTION_DOWN | 手指初次接触到屏幕时触发 |
ACTION_MOVE | 手指在屏幕上滑动时触发、会多次触发 |
ACTION_UP | 手指离开屏幕时触发 |
ACTION_CANECL | 事件被上层拦截时触发 |
1.2 控件的事件分发、拦截、消费
√表示有该方法,×表示没有该方法
类型 | 相关方法 | Activity | ViewGroup | View |
---|---|---|---|---|
事件分发 | dispatchTouchEvent | √ | √ | √ |
事件拦截 | onInterceptTouchEvent | × | √ | × |
事件消费 | OnTouchEvent | √ | × | √ |
整个 View 之间的事件分发,实质上就是一个大的递归函数,而这个递归函数就是 dispatchTouchEvent 方法。在这个递归的过程中会适时调用 onInterceptTouchEvent 来拦截事件,或者调用 onTouchEvent 方法来处理事件。用下面一段伪代码帮助我们更好的理解他们之间的调用关系:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
//ViewGroup是否拦截,若拦截则交给自己的onTouchEvent处理
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
//不拦截,则继续向子控件进行分发
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
1.3 大致流程图
当一个点击事件产生以后,它的传递或者说分发过程遵循如下顺序:Activity—>Window—>DecorView—>ViewGroup—>View, 如果一个View的onTouchEvent返回false,表示它没有消费事件,那么
2. Activity对点击事件分发过程
点击事件用MotionEvent来表示,当一个点击操作发生时,事件最先传递给当前 Activity,由Activity的dispatchTouchEvent来进行事件派发,具体的工作是由Activity内部 的Window来完成的。Window会将事件传递给decor view,decor view一般就是当前界面的底层容器(即setContentView所设置的View的父容器),通过 Activity.getWindow.getDecorView()可以获得。我们先从Activity的dispatchTouchEvent开始 分析。
Activity#dispatchTouchEvent ()
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
现在分析上面的代码。首先事件开始交给Activity所附属的Window进行分发,如果返 回true,整个事件循环就结束了,返回false意味着事件没人处理,所有View的 onTouchEvent都返回了false,那么Activity的onTouchEvent就会被调用。 接下来看Window是如何将事件传递给ViewGroup的。通过源码我们知道,Window是 个抽象类,而Window的superDispatchTouchEvent方法也是个抽象方法,因此我们必须找到Window的实现类才行。而我们知道由于Window的唯一实现是PhoneWindow,因此接下来看一下PhoneWindow是如何处 理点击事件的,如下所示。
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
到这里逻辑就很清晰了,PhoneWindow将事件直接传递给了DecorView,这个 DecorView是什么呢?请看下面:
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
// This is the top-level view of the window,containing the window decor. private DecorView mDecor;
@Override public final View getDecorView() {
if (mDecor == null) {
installDecor();