以下Coor=CoordinateLayout
首先事件到達CoordinateLayout,回調dispatchTouchEvent,CoordinateLayout並沒有重寫該方法,於是在ViewGroup中:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
.......
boolean handled = false;
// 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;
}
OK ,調用了onInterceptTouchEvent。這個方法在Coor類得到了 重寫
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
MotionEvent cancelEvent = null;
final int action = MotionEventCompat.getActionMasked(ev);
// Make sure we reset in case we had missed a previous important event.
if (action == MotionEvent.ACTION_DOWN) {
resetTouchBehaviors();
}
//關鍵方法
final boolean intercepted = performIntercept(ev, TYPE_ON_INTERCEPT);
if (cancelEvent != null) {
cancelEvent.recycle();
}
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
resetTouchBehaviors();
}
return intercepted;
}
關鍵方法:performIntercept(ev, TYPE_ON_INTERCEPT)
private boolean performIntercept(MotionEvent ev, final int type) {
boolean intercepted = false;
boolean newBlock = false;
MotionEvent cancelEvent = null;
final int action = MotionEventCompat.getActionMasked(ev);
final List<View> topmostChildList = mTempList1;
getTopSortedChildren(topmostChildList);//以繪製的先後順序取出子view(默認情況下)
// Let topmost child views inspect first
final int childCount = topmostChildList.size();
//遍歷所有的子view
for (int i = 0; i < childCount; i++) {
final View child = topmostChildList.get(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Behavior b = lp.getBehavior();//取得子view裏所使用的behavior
if ((intercepted || newBlock) && action != MotionEvent.ACTION_DOWN) {//不爲ACTION_DOWN 事件,那麼
// Cancel all behaviors beneath the one that intercepted. 如果有一個behavior對事件進行了攔截,就發送Cancel事件給後續的所有Behavior(其實這和一般的view的Intercept流程差不多)。假設之前還沒有Intercept發生,那麼所有的事件都平等地對所有含有behavior的view進行分發,現在intercept忽然出現,那麼相應的我們就要對除了Intercept的view發出Cancel
// If the event is "down" then we don't have anything to cancel yet.
if (b != null) {
if (cancelEvent == null) {
final long now = SystemClock.uptimeMillis();
cancelEvent = MotionEvent.obtain(now, now,
MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);構建ACTION_CANCEL事件
}
//根據傳入的參數不同回調相應事件
switch (type) {
case TYPE_ON_INTERCEPT:
b.onInterceptTouchEvent(this, child, cancelEvent);//注意是cancelEvent!
break;
case TYPE_ON_TOUCH:
b.onTouchEvent(this, child, cancelEvent);
break;
}
}
continue;
}
if (!intercepted && b != null) {
switch (type) {
case TYPE_ON_INTERCEPT:
intercepted = b.onInterceptTouchEvent(this, child, ev);
//注意intercepted變量在這裏有可能被更改!所以是否有intercepted的發生,完全取決於輪詢的behavior的方法onInterceptTouchEvent和下面的onTouchEvent是不是返回true,如果是,事件就會被他截取走,其他的behavior就不會收到進一步的事件,如果返回false,那麼事件還會繼續向其他behavior分發下去
break;
case TYPE_ON_TOUCH:
intercepted = b.onTouchEvent(this, child, ev);
break;
}
if (intercepted) {
mBehaviorTouchView = child;
}
}
// Don't keep going if we're not allowing interaction below this.
// Setting newBlock will make sure we cancel the rest of the behaviors.
final boolean wasBlocking = lp.didBlockInteraction();
final boolean isBlocking = lp.isBlockingInteractionBelow(this, child);
newBlock = isBlocking && !wasBlocking;
if (isBlocking && !newBlock) {
// Stop here since we don't have anything more to cancel - we already did
// when the behavior first started blocking things below this point.
break;
}
}
topmostChildList.clear();
return intercepted;
}
結束,這個時候就得看看Coor的子view們到底有沒有behavior了,如果有就會被依次回調behavior裏頭的相關方法
然而AppBarLayout的behavior並沒有重寫b.onTouchEvent和b.onInterceptTouchEvent。所以按照默認實現是返回false,沒有進行截取。
好了接下來ViewGroup開始依次調用子view的dispatchTouchEvent,就是正常的事件分發了,後面詳細說這個。
如果最終沒有子view接受事件,那麼就把事件傳回父類(就是viewgroup的父類 view)的dispatchTouchEvent,在那裏會調用onTouchEvent方法,而該方法也在Coor裏頭做了重寫,來看看
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
boolean cancelSuper = false;
MotionEvent cancelEvent = null;
final int action = MotionEventCompat.getActionMasked(ev);
//mBehaviorTouchView記錄的是:如果事件被某個子view的behavior截取的話,那麼這個view就記錄在這個變量裏頭,以後的事件都會傳遞給他。注意條件判斷是||,意味着如果存在這個記錄,那麼performIntercept()就根本不會被執行(因爲已經被截取了嘛)!
下面就會看到這個的用處
if (mBehaviorTouchView != null || (cancelSuper = performIntercept(ev, TYPE_ON_TOUCH))) {
// Safe since performIntercept guarantees that
// mBehaviorTouchView != null if it returns true
final LayoutParams lp = (LayoutParams) mBehaviorTouchView.getLayoutParams();
final Behavior b = lp.getBehavior();
if (b != null) {
handled = b.onTouchEvent(this, mBehaviorTouchView, ev);
}
}
// Keep the super implementation correct
if (mBehaviorTouchView == null) {
handled |= super.onTouchEvent(ev);
} else if (cancelSuper) {
if (cancelEvent == null) {
final long now = SystemClock.uptimeMillis();
cancelEvent = MotionEvent.obtain(now, now,
MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
}
super.onTouchEvent(cancelEvent);
}
if (!handled && action == MotionEvent.ACTION_DOWN) {
}
if (cancelEvent != null) {
cancelEvent.recycle();
}
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
resetTouchBehaviors();
}
return handled;
}
注意第一個if語 句裏頭的performIntercept方法
performIntercept(ev, TYPE_ON_TOUCH),這已經在上面分析過了這個函數,只是這裏傳入的參數是TYPE_ON_TOUCH 而上面傳遞的performIntercept(ev, TYPE_ON_INTERCEPT)。,參數不同進入的就是不同的switch case語句了.
整個流程下來,關鍵的就是兩次的performIntercept調用,第一次是在作爲ViewGroup分發事件之前進行一次performIntercept,第二次是在作爲一個view消費事件之前,也會進行一次