关于Android事件分发机制,一直半清不楚。Android事件传递机制——这篇博客实验做的很好,不过对于dispatchTouchEvent的返回值解释错误。直到看到这篇博文,才有了比较清晰的认识Android事件分发流程——这篇博客图文较多便于理解但是没有涉及原理。这篇文章对原理讲解的比较清楚以伪代码的形式展示事件分发结果,配合第二篇博客效果非常好
总结:
1、Activity既不是View也不是ViewGroup,但是它有dispatchTouchEvent,因为Activity实现了Window.Callback并在Activity的attach方法中设置了Activity作为PhoneWindow的回调,也就是PhoneWindow内部类DecorView的实际回调。
而触摸事件实际上是由WindowManagerService捕捉并包装成InputChannel传递到ViewRootImpl的WindowInputEventReceiver,继而提交给了DecorView(ViewRootImpl中的mView就是DecorView)。
这里就要提到PhoneWindow、DecorView和ViewRootImpl的关系。Activity的setContentView方法实际上调用的就是PhoneWindow的setContentView,而这个方法会调用installDecor生成DecorView,这样PhoneWindow就和DecorView互相关联起来了。而ViewRootImpl却是在ActivityThread的performResumeActivity中调用Activity的makeVisible中创建并关联的。
Android触摸事件来源
这样Activity实现dispatchTouchEvent和onTouchEvent也就情有可原了。这里为什么不直接从DecorView往子ViewGroup和子View传递的原因就不清楚了,上文说的给DecorView最后一次处理TouchEvent的机会领悟不了。
2、dispatchTouchEvent表示分发事件。它在Activity、View和ViewGroup中的表现都不一样。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
可以看出在Activity中,如果重写了此方法且没有调用super,即没用调用默认逻辑。那么触摸事件根本不会继续传递,也不会触发onTouchEvent。
如果没有重写,则会调用DecorView的superDispatchTouchEvent其实就是ViewGroup的dispatchTouchEvent继续分发。
ViewGroup的dispatchTouchEvent源代码很长,这里截取关键部分。
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
可以看到ViewGroup会先调用onInterceptTouchEvent判断是否拦截事件。如果没有拦截,从后往前遍历每个子View调用dispatchTransformedTouchEvent分发事件。如果拦截了或者子View的dispatchTouchEvent返回false,则调用自身的onTouchEvent。
再看View的dispatchTouchEvent。
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
可以看出,如果View设置了有效onTouchListener,并且View没有被禁用,那么监听器的onTouch方法就会被触发。如果onTouch方法返回true,那么onTouchEvent就不会执行,View的onClick就不会触发了。也就是说OnTouchListener和OnClickListener可以同时工作,但如果OnTouchListener的onTouch返回true,那么OnClickListener就不能工作。