扫盲细节,Android 的事件分发机制

事件都是从用户按下(ACTION_DOWN)的那一刻产生的,三个非常重要的与事件相关的方法:

  • dispatchTouchEvent()
  • onTouchEvent()
  • onInterceptTouchEvent()

Activity 的事件分发机制

从单词含义已经很明显的知道,dispatchTouchEvent() 是负责事件分发的。当点击事件产生后,事件首先会传递给当前的 Activity,这会调用 Activity 的 dispatchTouchEvent() 方法,看看源码中是怎么处理的。

扫盲细节,Android 的事件分发机制

由于我们一般产生点击事件都是 MotionEvent.ACTION_DOWN,所以一般都会调用到 onUserInteraction() 这个方法。

扫盲细节,Android 的事件分发机制

很遗憾,这个方法实现是空的,不过可以从注释和其他途径可以了解到,该方法主要的作用是实现屏保功能,并且当此 Activity 在栈顶的时候,触屏点击 Home、Back等都会触发这个方法。

再来看看第二个 if 语句,getWindow().superDispatchTouchEvent()getWindow() 明显是获取 Window,由于 Window 是一个抽象类,所以我们能拿到其子类 PhoneWindow,直接看看 PhoneWindows.superDispatchTouchEvent() 到底做了什么操作。

扫盲细节,Android 的事件分发机制

直接调用了 DecorView 的 superDispatchTrackballEvent() 方法。DecorView 继承于 FrameLayout,而 FrameLayout 作为 ViewGroup 的子类,所以直接调用了 ViewGroup 的 dispatchTouchEvent()

ViewGroup 的事件分发机制

查看 ViewGroup 的 dispatchTouchEvent() :

扫盲细节,Android 的事件分发机制

注意其中红框里面的代码,看注释也能知道,定义了一个 boolean 值变量 intercept 来表示是否要拦截事件。

其中采用到了 onInterceptTouchEvent(ev) 对 intercept 进行赋值。大多数情况下,onInterceptTouchEvent() 返回值为 false,但我们完全可以通过重写 onInterceptTouchEvent(ev) 来改变它的返回值,继续往下看后面对这个 intercept 做了什么处理。

扫盲细节,Android 的事件分发机制

暂时忽略 判断的 canceled,该值同样大多数时候都返回 false,所以当我们没有重写 onInterceptTouchEvent() 并使它的返回值为 true 时,一般情况下都是可以进入到该方法的。

继续阅读源码可以发现,里面做了一个 for 循环,通过倒序遍历 ViewGroup 下面的所有子 View,然后一个一个判断点击位置是否是该子 View 的布局区域,当然还有一些其他的,由于篇幅原因,这里就不细讲了。

View 的事件分发机制

ViewGroup 说到底还是一个 View,所以我们不得不继续看看 View 的 dispatchTouchEvent()

扫盲细节,Android 的事件分发机制

  • (mViewFlags & ENABLED_MASK) == ENABLED
    判断当前点击的控件是否为 enable,但由于基本 View 都是 enable 的,所以这个条件基本都返回 true。

  • mOnTouchListener.onTouch(this, event)
    即我们调用 setOnTouchListener() 时必须覆盖的方法 onTouch() 的返回值。

从上述的分析,终于知道「onTouch() 方法优先级高于 onTouchEvent(event) 方法」是怎么来的了!!

再看 onTouchEvent()

扫盲细节,Android 的事件分发机制

从上面的代码可以明显地看到,只要 View 的 CLICKABLE 和 LONG_CLICKABLE 有一个为 true,那么 onTouchEvent() 就会返回 true 消耗这个事件。CLICKABLE 和 LONG_CLICKABLE 代表 View 可以被点击和长按点击,我们通常都会采用 setOnClickListener() 和 setOnLongClickListener() 做设置。接着在 ACTION_UP 事件中会调用 performClick() 方法。

扫盲细节,Android 的事件分发机制

从截图中可以看到,如果 mOnClickListener 不为空,那么它的 onClick() 方法就会调用。

总结

扫盲细节,Android 的事件分发机制

  • Activity 的事件分发示意图

扫盲细节,Android 的事件分发机制

  • ViewGroup 事件分发示意图

扫盲细节,Android 的事件分发机制

  • View 的事件分发示意图

    扫盲细节,Android 的事件分发机制

  • 事件分发工作流程总结

扫盲细节,Android 的事件分发机制