这里记录一下关于ViewGroup中下发不同Action导致系统分发逻辑不同的总结,该总结建立在阅读过事件分发的源码上,如果没有阅读过源码的话,不建议阅读该篇文章。如果有总结不对的地方,希望各位同行能够指正,一起进步~
首先ViewGroup中的触摸处理逻辑如下:
void dispatchTouchEvent(){
if(onInterceptionTouchEvent()){
onDraw();
}else{
child.disptachTouchEvent();
}
}
当一个触摸事件来临的时候,首先由Activity接收到触摸事件,然后下发至DecorView,DecorView中根据Z轴位置由上至下进行传递事件处理:
- Activity -> DecorView -> ViewGroup ->....->View
当传递过程中没有任何View消费事件,那么事件由View->ViewGroup->Activity逆序返回,最终的事件由Activity处理,如果View消费了事件,那么则分为两种情况,这两种情况可以从代码的角度中理解:
public boolean dispatchTouchEvent(MotionEvent ev){
...
//1.
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 {
intercepted = true;
}
//2.
if (!canceled && !intercepted){
//传递事件,构造mFirstTouchTarget
}
...
}
- 当在ACTION_DOWN时拦截,第二步是不会走的,也就意味着mFirstTouchTarget=null,在随后的事件来临的时候,由于mFirstTouchTarget==null,事件全权交由当前ViewGroup处理,子View就再也收不到触摸事件了。
- 当不在ACTION_DOWN时拦截,并且mFirstTouchTarget不为空,那么这个时候子View会收到一个ACITON_CANCEL事件,然后后续事件都由ViewGroup处理了。
那么通过这种情况,我们得知,当我们在处理滑动触摸冲突的时候,不应该在父ViewGroup中进行ACTION_DOWN的拦截操作,如果拦截了,子View根本不可能受到触摸事件;另外当父ViewGroup拦截中拦截了ACTION_MOVE操作后,实际上子View是会收到一个CANCALE事件的,通过该事件,我们能在子View中进行一些善后的处理。