事件分发在android中非常重要,写了3篇文章总结其中的故事
android事件分发(一)
android事件分发(二)源码源码
android事件分发(三)重要的函数requestDisallowInterceptTouchEvent
前面我们说过,儿子吃到肉了,父亲还可能抢那么儿子有没有办法不让父亲抢呢,有?
可以通过调用mParent.requestDisallowInterceptTouchEvent(true),之后parent就默认不拦截事件了。
这个故事就是,儿子吃到好吃的了,说,爸爸不许抢,然后爸爸就不会再抢了。
这是怎么实现的呢?
儿子掉mParent.requestDisallowInterceptTouchEvent(true)
我们看viewgroup的requestDisallowInterceptTouchEvent方法干了什么?
这个函数有个bool参数,我们先看为true的情况,非常简单,2件事情,一件是把mGroupFlags里面的某一位置成true,然后再调用parent的requestDisallowInterceptTouchEvent(true)方法。总的来说就是把本viewgroup以及祖先viewgroup的那一个标志位都置为true。
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
// We're already in this state, assume our ancestors are too
return;
}
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// Pass it up to our parent
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
置为true有什么用呢?回头看看上篇文章的ViewGroup:: dispatchTouchEvent的代码,有这么一段。看下就懂了,mGroupFlags & FLAG_DISALLOW_INTERCEPT为true,那就会disallowIntercept 为true,intercepted 就为false,不会拦截,这下连起来了,mParent.requestDisallowInterceptTouchEvent(true)会导致parent下次不拦截事件。
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;
}
那么parent是否永远无法拦截了呢?并不是,只是在一个cycle内无法拦截,等到下一次down事件来的时候,这个标志位会重置。代码在下边,可以看到mGroupFlags的FLAG_DISALLOW_INTERCEPT标志位会被清零。
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
/**
* Resets all touch state in preparation for a new cycle.
*/
private void resetTouchState() {
clearTouchTargets();
resetCancelNextUpFlag(this);
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
mNestedScrollAxes = SCROLL_AXIS_NONE;
}
1、 子view不希望父view拦截事件可以调用mParent.requestDisallowInterceptTouchEvent(true)
2、 viewgroup的mGroupFlags在下一个cycle来临的时候,FLAG_DISALLOW_INTERCEPT标志位会被清零
3、 requestDisallowInterceptTouchEvent这个函数一般不是自己调用的,而是给儿子调用的
4、 requestDisallowInterceptTouchEvent是解决滑动冲突的大杀器,目前大部分原生控件都是使用mParent.requestDisallowInterceptTouchEvent(true)来解决滑动冲突的。如何解决滑动冲突,可以看下一节