为了解决滑动冲突可以采用外部拦截法,就是重写父布局的onIntercepteTouchEvent方法:ACTION_DOWN 不拦截,ACTION_MOVE,达到父元素滑动条件就拦截,达不到就不拦截
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
var intercpted = false
var x = ev?.x
var y = ev?.y
when(ev?.action){
MotionEvent.ACTION_DOWN->{
intercpted = false
}
MotionEvent.ACTION_MOVE->{
if(满足父元素的条件){
intercepted = true
}else{
intercepted = false
}
}
MotionEvent.ACTION_UP->{
intercpted = false
}
}
Log.e("gacmy","ViewGroup2 onInterceptTouchEvent:${intercpted}")
return intercpted
}
分析1:为什么ACTION_DOWN 不拦截?
查看ViewGroup dispatchTouchEvent源码可以知道ACTION_DOWN拦截以后,后续事件的派发都不会再传递给子View
分析2,如果ACTION_MOVE 满足父布局的滑动条件,拦截一次之后,再没有触发下一次ACTION_DOWN事件情况下,即使再次满足子布局的滑动条件,子布局也不会再次受到任何事件。
if (!canceled && !intercepted) {
...
这里是进行ACTION_DOWN事件派发的操作,
如果intercepted 是true的话,这里就不会再执行
...
}
//如果ACTION_DWON事件被拦截,则子View一定不会存在mFirstTouchTarget链表中
//这里进行ACTION_MOVE ACTION_UP事件的派发
//ViewGroup只有一个子View ACTION_DOWN被拦截之后,mFirstTouchTarget == null
//后续事件都只会传递给父布局自己的onTouch事件里面
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget target = mFirstTouchTarget;
//这里处理ACTION_MOVE 事件和ACTION_UP事件
while (target != null) {
//每一次 dispatchTouchEvent执行的时候 newTouchTarget = null
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
//ACTION_MOVE 滑动父布局intercepted返回true,cancelChild这里返回true
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if(dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {
handled = true;
}
//cancelChild = true
//子布局的TouchTarget会在链表中被删除。
//父布局只有一个子View的情况下,mFirstTouchTarget = null
//下一次即使满足子布局的滑动条件,intercepted = false。也不会在给他传递任何事件了。
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}