寫這邊文章的初衷是由於常常有人看到說,父viewGroup的onInterceptTouchEvent返回true,子view調用requestDisallowInterceptTouchEvent(true)爲何還能實現子view能夠捕獲點擊事件(其實這個問題一開始是錯了),另外有人問用requestDisallowInterceptTouchEvent(true)到底應該在何時調用,是在子view的dispatchTouchEvent,onTouchEvent,onInterceptTouchEvent哪一個方法,又應該在MotionEvent的Down,Up,Move的何時調用呢?其實這些問題根本仍是對手勢衝突有一點了解,可是又沒理解透徹致使的,因此就有了這篇文章,但願有必定的啓示做用。git
答:這個問題一開始就是錯的,當咱們的父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
答:在默認的狀況下,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
答:綜上所述,咱們一個手勢的操做,會經歷down,move,up等操做,子view調用requestDisallowInterceptTouchEvent(true)的時間,是必須在能拿到點擊事件,好比咱們在down的時候調用了方法,接下來的move,up都會傳到子view上了,若是是在子view的move方法中調用的話,那麼要確認父view在move的過程當中,能將事件傳遞給子view就行了。code