以前看了很多人介绍的Android事件派发流程,但最近使用那些来写代码的时候出现了不少错误。所以回顾一下整个流程,简单介绍从手触摸屏幕开始到事件在View树派发。从源码上分析ViewGroup.dispatchTouchEvent。
事件从触摸到View简述
Android的事件产生是从我们触摸屏幕开始,在经过Input子系统,最后达到我们的应用程序(或者经过WindowManagerService到达应用程序)。
而其中Input子系统在Java层对应着InputManagerService,其主要在native层,由InputReader读取EventHub的元数据,将这些数据加工成InputEvent,最后发到InputDispatcher,而InputDispatcher则负责将时间发到应用程序,Input子系统流程可以参见这篇文章Android Framework——之Input子系统。
对于应用层的时间流程,主要是下面的流程图所示:
其中最后一步就是我们经常说的View事件派发流程。另外上面DecorView是经过了两次,第一次是调用DecorView的dispatchTouchEvent,它的源码是:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}
Callback就是Window.Callback,Activity实现了这个接口。在Activity的attach函数中,会调用window的setCallback,将Activity设置给Window。所以这里getCallback返回的就是Activity,最终会调用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);
}
在ACTION_DOWN的时候会调用onUserInteraction方法,然后调用Window(实际上是PhoneWindow)的superDispatchTouchEvent,如果Window的superDispatchTouchEvent消耗了事件,则直接返回,不会调用Activity的onTouchEvent方法。
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
而DecorView的superDispatchTouchEvent为:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
最终还是调用DecorView的父类的dispatchTouchEvent,DecorView的父类是FrameLayout,它没实现该方法,最终会调用ViewGroup的dispatchTouchEvent方法。从这里开始就进入了view树的时间派发流程了。
View树的事件派发流程
这里从源码上分析事件派发的一些特性。事件派发最开始会进入到ViewGroup的dispatchTouchEvent(DecorVIew父类),下面是ViewGroup的dispatchTouchEvent伪代码的分析,直接在对应的代码部分加了注释:
“`
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//一开始做一些调试验证,另外如果事件的目标是focused view,并且当前view就是一个focused view,
//有可能view的子View就会处理这次事件,所以将targetAccessibilityFocus设置为false。