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

相關文章
相關標籤/搜索