从ViewRootImpl到Decorview, 从DecorView交给Activity, Activity通过windowphone从DecorView开始分发.
自上而下思维,本文梳理枝干,不关注太细节的东西。
java
#DecorView.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
//mWindow.getCallback()获取的就是Activity本身.
scss
#Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
csharp
#PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
csharp
#DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用父类ViewGroup的分发方法开始真正View体系中的事件分发
return super.dispatchTouchEvent(event);
}
后面就是ViewGroup的具体分发流程.
VG重写了继承的父类View的dispatchTouchEvent()
return true则代表消费时间,停止继续分发。反之则反之。
关键核心代码。
java
#ViewGroup.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!canceled && !intercepted) {
//如果不拦截,会遍历子View进行分发事件
for (int i = childrenCount - 1; i >= 0; i--) {
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//找到了需要消费的子View--child,下面代码会将mFirstTouchTarget赋值为该child
newTouchTarget = addTouchTarget(child, idBitsToAssign);
}
}
...
//如果拦截或者没有子View消费事件,此处mFirstTouchTarget会为空,
//会将child参数设为null。其实就是看vg自己是否要消费事件
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
//如果mFirstTouchTarget!=null,说明没有拦截并且子View消费了事件,
//那么该vg的事件分发其实已经在上面整个流程的代码中走完,下面是一些杂项处理。
...
return handled;
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
...
if (child == null) {
//child为空,说明VG拦截了事件或者没有找到可以消费事件的子View,
//那么就调用父类View的dispatchTouchEvent()来自己处理
handled = super.dispatchTouchEvent(event);
} else {
//此处将事件分发给子View,如果所有子View都不处理那么其实又会
//调用上面child == null的代理,结合上面dispatchTouchEvent中流程去理解。
//如果子View也是个vg,那么又会调用该vg的dispatchTouchEvent()。嵌套调用
handled = child.dispatchTouchEvent(event);
}
...
return handled;
}
VG先判断是否拦截,如果拦截
则交个自己重写的的dispatchtouchevent()处理,依次调用onTouchListener.onTouch()和onTouchEvent(),onTouchEvent()内部又会调用onClickListerner.click()的回调如果前面的调用消费了事件则不会走到后面。 如果本身并没有消费事件,那么返回false
向上传递.
ini
#View.java
public boolean dispatchTouchEvent(MotionEvent event) {
...
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消费了事件,那么vg会记录该view,后面的事件都会交给其处理.
VG如果不拦截事件
那么VG就会遍历所有子View,交给子View的dispatchtouchevent()处理。内部流程和上面VG调用父类View的dispatchTouchEvent()是一样的。 依次调用onTouchListener.onTouch()和onTouchEvent(),onTouchEvent()内部又会调用onClickListerner.click()的回调如果前面的消费则不会走到后面。
VG判断所有子view都未消费事件,则会判断自身是否需要消费,交给自己继承的父类View的dispatchtouchevent(),也就是调用super.dispatchtouchevent()方法,这和上面拦截时流程又一致了。如果VG本身也都没有消费,那么返回false。
继续向上传递. 重复此过程
如果都未消费,最后到达Activity,如果也未消费,直到又回到DecorView.最终到达ViewRootImpl的责任链的下一个节点继续处理。
如有出入的部分,还请指教