以下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消费事件之前,也会进行一次