事件分發機制是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邏輯運算相關的知識。