requestDisallowInterceptTouchEvent调用时机分析

1.引言

写这边文章的初衷是由于常常有人看到说,父viewGroup的onInterceptTouchEvent返回true,子view调用requestDisallowInterceptTouchEvent(true)为何还能实现子view能够捕获点击事件(其实这个问题一开始是错了),另外有人问用requestDisallowInterceptTouchEvent(true)到底应该在何时调用,是在子view的dispatchTouchEvent,onTouchEvent,onInterceptTouchEvent哪一个方法,又应该在MotionEvent的Down,Up,Move的何时调用呢?其实这些问题根本仍是对手势冲突有一点了解,可是又没理解透彻致使的,因此就有了这篇文章,但愿有必定的启示做用。git

2.Q1:父viewGroup的onInterceptTouchEvent返回true,子view调用requestDisallowInterceptTouchEvent(true)为何还能实现子view能够捕获点击事件?

答:这个问题一开始就是错的,当咱们的父ViewGroup无论在MotionEvent的哪一种状态都返回true的话,子view是直接拿不到任何事件了,也就是说子view的dispatchTouchEvent,onTouchEvent,onInterceptTouchEvent都不会执行。为何这样呢?看下代码ViewGroup的dispatchTouchEvent方法:github

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();//1
            }

            // Check for interception.
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {//2
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } 
            ....
            
             if (!canceled && !intercepted) {//3
             }

注释1:当ViewGroup拿到点击Down事件的时候,会重置mGroupFlags的值mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT,当执行boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;mGroupFlags & ~FLAG_DISALLOW_INTERCEPT& FLAG_DISALLOW_INTERCEPT很明显这个值就是为0,因此disallowIntercept为false,必然会进if的判断。
注释2:Intercepted的值被赋予了onInterceptTouchEvent(ev)的返回值,若是都是为true的话,那么注释3的if判断始终都进不去了,那么子view的一切点击事件都被拦截了。app

2.Q2:按照上面的所说的,是否是子view调用requestDisallowInterceptTouchEvent(true)并非万能,原本就不是万能,为何我看到好像requestDisallowInterceptTouchEvent(true)均可以让子view拿到点击事件了?

答:在默认的状况下,ViewGroup的onInterceptTouchEvent的方法是这样的:ide

public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        return false;
    }

很明显当咱们在手机上点击的时候,来源不是mouse的时候,返回的是false,觉得这在下面这段代码当中,intercepted是为false的。this

if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {//2
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            }

那这个时候Action_Down事件就被子View拦截到了,若是这个时候,咱们在子view的Down事件里面调用requestDisallowInterceptTouchEvent(true)spa

@Override
    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;//1
        } else {
            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;//2
        }

        // Pass it up to our parent
        if (mParent != null) {
            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
        }
    }

其实在这个方法中最重要的仍是对mGroupFlags赋值,当为true的时候, mGroupFlags |= FLAG_DISALLOW_INTERCEPT,接下来,当move事件的时候,final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0的计算变成mGroupFlags | FLAG_DISALLOW_INTERCEPT & FLAG_DISALLOW_INTERCEPT这个的结果仍是FLAG_DISALLOW_INTERCEPT,这个值在初始化的时候是protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;,因此不等于0,进而disallowIntercept为true,直接进入else方法,intercepted = false;接下来的move,up事件都能传递到子view上去了。rest

3.Q3调用时机?

答:综上所述,咱们一个手势的操做,会经历down,move,up等操做,子view调用requestDisallowInterceptTouchEvent(true)的时间,是必须在能拿到点击事件,好比咱们在down的时候调用了方法,接下来的move,up都会传到子view上了,若是是在子view的move方法中调用的话,那么要确认父view在move的过程当中,能将事件传递给子view就行了。code

4.demo:stickScrollView

相关文章
相关标签/搜索