源碼分析爲什麼requestDisallowInterceptTouchEvent(true)能阻止父View攔截事件

  事件分發機制是Android中的一個難點,但是現在很多人都在寫關於Android事件分發機制的文章,並且講的都很不錯,很多人也基本明白了事件是首先一級一級向下分發(如果父View不攔截的話,即父View的onInterceptTouchEvent方法返回false),交由子View去處理,然後子View再將事件的處理結果一級一級向上反饋,子View沒有處理完(子View的onTouchEvent方法返回了false),那麼事件又會向上傳遞給父View,交由父View進行處理。
  很多時候子View並不希望父View攔截事件,這時我們該怎麼做能夠阻止父View攔截事件呢?首先我們看下ViewGroup的dispatchTouchEvent方法的部分源碼:

// Check for interception.
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;
}

  從源碼中我們可以看到這一句:
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
首先我們可以看到mGrouipFlag的聲明protected int mGroupFlags;它並沒有被賦值,因此默認爲0,而FLAG_DISALLOW_INTERCEPT默認爲0x80000,因此默認情況下(mGroupFlags & FLAG_DISALLOW_INTERCEPT) = 0,所以disallowIntercept爲false,!disallowIntercept就爲true了,因此代碼接着向下執行intercepted = onInterceptTouchEvent(ev);即我們的父View會默認執行攔截事件。那我們怎麼能夠讓父View默認不執行攔截事件呢,我們很多人都知道調用requestDisallowInterceptTouchEvent(true)方法就能夠阻止父View攔截事件。那麼爲什麼呢,接下來我們就從源碼的角度來講解這一句到底做了什麼讓父View就不攔截事件了,我們採用逆推的方法來說明。
  要想讓dispatchTouchEvent方法中intercepted = onInterceptTouchEvent(ev);這一句不執行,必須的保證disallowIntercept 爲 true,即(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;成立,現在我們就只要證明爲什麼(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;一定成立,接着我們就要看requestDisallowInterceptTouchEvent()方法的源碼了:

/**
 * {@inheritDoc}
 */
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);
    }
}

我們設置了參數disallowIntercept 爲 true,首先我們看到該方法中的第一個判斷

if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
        // We're already in this state, assume our ancestors are too
        return;
    }

1、假設(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0) 爲 true,此時if語句的條件成立,然後就return了,接着回到dispatchTouchEvent方法中,父View不攔截事件的要求就是要保證(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;明顯成立,所以父View將不會攔截事件。
2、假設(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0) 爲 false,因爲disallowIntercept爲true,因此會接着向下執行mGroupFlags |= FLAG_DISALLOW_INTERCEPT;即mGroupFlags = mGroupFlags | FLAG_DISALLOW_INTERCEPT;,然後我們回到dispatchTouchEvent方法中,父View不攔截事件的要求是要保證(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;即(mGroupFlags | FLAG_DISALLOW_INTERCEPT & FLAG_DISALLOW_INTERCEPT) != 0成立,而這個又等價於 FLAG_DISALLOW_INTERCEPT != 0,顯然FLAG_DISALLOW_INTERCEPT是不等於0的,所以條件成立,父View不會攔截事件。
  至於爲什麼(mGroupFlags | FLAG_DISALLOW_INTERCEPT & FLAG_DISALLOW_INTERCEPT) != 0等價於FLAG_DISALLOW_INTERCEPT != 0,即爲什麼a | b & b = b,有興趣的同學可以去了解java邏輯運算相關的知識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章