前面的博客中,我们通过例子分析了一下Android中事件传递的流程,
详细内容可以参考:Android触摸事件传递机制简要分析
贯穿整个Android的触摸事件分发的流程,基本可以抽象成以下的伪代码:
public boolean dispatchTouchEvent(MotionEvent ev){
boolean handle = false;
if(onInterceptTouchEvent(ev)){
handle = onTouchEvent(ev);
}else{
handle = child.dispatchTouchEvent(ev);
}
return handle;
}
如果一个事件传递到了ViewGroup处,首先会判断当前ViewGroup是否要拦截事件,
即调用onInterceptTouchEvent()方法。
如果返回true,则表示ViewGroup拦截事件,
那么ViewGroup就会调用自身的onTouchEvent来处理事件;
如果返回false,表示ViewGroup不拦截事件,
此时事件会分发到它的子View处,即调用子View的dispatchTouchEvent方法。
如此反复直到事件被消耗掉。
本篇博客,我们以Android O的代码为例,看看对应流程的源码。
考虑到事件分发的流程较为繁琐,本篇博客主要针对Activity和ViewGroup部分。
一、Activity部分
系统有一个线程在循环收集屏幕硬件信息。
当用户触摸屏幕时,该线程会把从硬件设备收集到的信息,
封装成一个MotionEvent对象,然后把该对象存放到一个消息队列中。
与此对应,系统的另一个线程循环的读取消息队列中的MotionEvent,然后交给WMS去派发。
WMS把该事件派发给当前处于Active状态的Activity,即处于活动栈最顶端的Activity。
我们就从Activity的dispatchTouchEvent入手,看看整个事件分发的流程:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//空实现,其用途注释已经说的比较清楚了
//若需要了解用户点击界面,可以自行实现该接口
//Implement this method if you wish to know that the user has
//interacted with the device in some way while your activity is running.
onUserInteraction();
}
//首先进行事件分发,事件被消费掉就会返回true
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//如果事件没有没消费掉,那么就会调用Activity的onTouchEvent
return onTouchEvent(ev);
}
从上面的代码容易看出,Activity收到触摸事件后首先会进行分发;
如果事件在分发的过程中没被消费掉,最终会被Activity的onTouchEvent处理。
在继续分析之前,我们先看看Activity的getWindow函数:
public Window getWindow() {
//返回Activity持有的mWindow对象
return mWindow;
}
mWindow对象是Activity显示在界面上时创建的,如下所示:
final void attach(.....) {
........
//实际上是一个PhoneWindow对象
mWindow = new PhoneWindow(this, window, activityConfigCallback);
........
}
由上面的代码易知,Activity分发的事件实际上交给了PhoneWindow。
对应分发事件的函数如下:
public boolean superDispatchTouchEvent(MotionEvent event) {
//mDecor的类型为DecorView,在PhoneWindow初始化时得到
return mDecor.superDispatchTouchEvent(event);
}
DecorView继承FrameLayout,后者继承ViewGroup,
于是上述代码最终会将触摸事件递交给ViewGroup处理。
二、ViewGroup部分
接下来我们跟进ViewGroup的dispatchTouchEvent函数。
2.1 处理Accessibility Focus事件
public boolean dispatchTouchEvent(MotionEvent ev) {
.......
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
.......
ViewGroup收到触摸事件后,首先需要处理特殊情况,
即携带FLAG_TARGET_ACCESSIBILITY_FOCUS的MotionEvent。
该标志位的含义可以参考注释:
/**
* 1、 Private flag indicating that this event was synthesized by the system and
* should be delivered to the accessibility focused view first.
*
* 2、 When being dispatched such an event is not handled by predecessors of the accessibility
* focused view and after the event reaches that view the fla