类似的场景非常之多,例如,父容器需要水平滑动,子View需要垂直滑动等,一般我会重写父容器的
onInterceptTouchEvent
方法,如果手势水平移动,则父容器拦截事件,onInterceptTouchEvent
方法返回true.
这里就分析上面的场景
- Down下来时候, ViewGroup不拦截该事件,最终结果是
mFirstTouchTarget
不等于null了. - 当手势为上下滑动时,ViewGroup不拦截事件,并且事件会继续传到
onInterceptTouchEvent
中来,后续事件由dispatchTransformedTouchEvent
分发到子View中
- 当手势为左右滑动时,ViewGroup会拦截事件.
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//事件被拦截之后,intercepted = true
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
//事件被拦截之后,mFirstTouchTarget此时的值为子View,还不是null,直接走到else中.
if (mFirstTouchTarget == null) {
...
} else {
...
//因为 intercepted = true, 所以cancelChild为true.看看dispatchTransformedTouchEvent方法中,如果cancelChild为true时候会做什么.
final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {
handled = true;
}
...
}
- 子View为何会收到
MotionEvent.ACTION_CANCEL
事件
//看看cancel 为true时候会做什么.
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
//cancel为true
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
//会将该事件设置为MotionEvent.ACTION_CANCEL
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
//再将该事件分发到子View中, 子View就会收到MotionEvent.ACTION_CANCEL事件
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
//子View会返回true,消耗掉该事件.
return handled;
}
}
- 事件如何分发到ViewGroup中
//在ViewGroup中,dispatchTouchEvent方法中最后执行的几行代码会将mFirstTouchTarget置为null
//如此,当非Down事件进来之后会直分发到ViewGroup中.
public boolean dispatchTouchEvent(MotionEvent ev) {
...
//若mFirstTouchTarget等于null,会执行dispatchTransformedTouchEvent方法,参数View 为null.
//这样事件就被分发到
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
final TouchTarget next = target.next;
// 这里就表示为Down以外的事件分发流程了.
// intercepted表示ViewGroup是否拦截该次事件.
// 拦截时intercepted=true则cancelChild=true,不拦截时intercepted=false则cancelChild=false.
final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
// 这里是将事件分发到目标子View中,dispatchTransformedTouchEvent()下面分析.
if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {
// 如果目标子View消耗了该事件,ViewGroup.dispatchTouchEvent()就返回true.
handled = true;
}
// 如果ViewGruop拦截事件,则cancelChild=true.
if (cancelChild) {
// 这句话意思是mFirstTouchTarget将被他的上一个TouchTarget取代.TouchTarget是一个链表结构.
// mFirstTouchTarget对象中的当前目标子View对象,今后将不会在接收到当前的事件序列中的任何事件了.
// 假如当前mFirstTouchTarget上一个TouchTarget对象为null,那么mFirstTouchTarget也将为null,那么当该事件序列中的下一个事件到来时,ViewGroup将会把该事件交由自己处理.
mFirstTouchTarget = next;
}
}
}