分2种:内部拦截和外部拦截
外部拦截
viewgroup用onInterceptTouchEvent处理,对需要的事件返回true拦截掉,子节点再也没有机会接触到事件,所有事件自己的ontouch处理。
内部拦截
子view通过requestDisallowInterceptTouchEvent可以让父view不再拦截事件
前提是down事件发生的时候viewgroup不能拦截,如果拦截了,view根本就没机会收到事件。
如果down事件到达了子view,viewgroup拦截move事件等,这种情况下,子view用requestDisallowInterceptTouchEvent优先在viewgroup的拦截前不允许拦截,就把move事件还是发送到子view里去。
以viewgroup解释
起点肯定是dispatchTouchEvent
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState();
}
//清除掉内部的TouchTarget链表,如果TouchTarget内有节点,发送当前事件给他们然后清理
//如果是ACTION_DOWN,做拦截判断,先用requestparentIntercept的标识判断
//后自己的onInterceptTouchEvent判断
//或者mFirstTouchTarget有值,说明自己在处理,这2种情况说明拦截了
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;
}
down事件先清除了mFirstTouchTarget,所以down进入后后续的进入,都会判断是否被子类要求不拦截,子类要求不拦截后,才能走自己的onInterceptTouchEvent。
后续判断是否做了取消动作,
如果不取消不拦截一个分支:final ArrayList<View> preorderedList = buildTouchDispatchChildList();建立子child表用Z轴排序
dispatchTransformedTouchEvent去子child分发
如果分发成功,使用addTouchTarget加入一个TouchTarget节点
后续使用mFirstTouchTarget来判断,mFirstTouchTarget空说明没有加入到节点,说明没有child处理,吧自己当作普通view调用dispatchTransformedTouchEvent,由于内部没有child调用view.dispatchTouchEvent最终去触发ontouch了。
dispatchTransformedTouchEvent的结果被作为viewgroup.dispatchTouchEvent返回
这一系列就循环了。
TouchTarget是一个类似链表结构的有下级引用的内部静态类
private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
addTouchTarget指出 mFirstTouchTarget是TouchTarget链表的表头,操作都在表头,就是一个栈
分发事件给子节点,没有子节点把自己当一个viwe处理,否则给子节点处理
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
}