Behavior与事件处理
Behavior提供了onInterceptTouchEvent和onTouchEvent两个方法来处理触摸事件。有了这两个方法,自定义事件处理就不用去继承View重写事件处理的相关方法了。
CoordinatorLayout在onInterceptTouchEvent和onTouchEvent两个方法中把事件交给了子View的Behavior进行处理。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
MotionEvent cancelEvent = null;
final int action = MotionEventCompat.getActionMasked(ev);
// 如果action是ACTION_DOWN,则重置之前的触摸行为,以免之前有遗漏。
if (action == MotionEvent.ACTION_DOWN) {
resetTouchBehaviors();
}
// 把事件交给子View的Behavior的performIntercept方法处理。
final boolean intercepted = performIntercept(ev, TYPE_ON_INTERCEPT);
if (cancelEvent != null) {
cancelEvent.recycle();
}
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
resetTouchBehaviors();
}
// 返回子View的Behavior的performIntercept方法处理结果。
return intercepted;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
boolean cancelSuper = false;
MotionEvent cancelEvent = null;
final int action = MotionEventCompat.getActionMasked(ev);
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) {
// 调用Behavior的onTouchEvent方法
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;
}
// 重置当前的触摸行为,为新的事件流做好准备
private void resetTouchBehaviors() {
// 给当前处理事件的Behavior发生CANCEL事件,结束事件流
if (mBehaviorTouchView != null) {
final Behavior b = ((LayoutParams) mBehaviorTouchView.getLayoutParams()).getBehavior();
if (b != null) {
final long now = SystemClock.uptimeMillis();
final MotionEvent cancelEvent = MotionEvent.obtain(now, now,
MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
b.onTouchEvent(this, mBehaviorTouchView, cancelEvent);
cancelEvent.recycle();
}
mBehaviorTouchView = null;
}
// 重置每个子View的mDidBlockInteraction为false。
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.resetTouchBehaviorTracking();
}
mDisallowInterceptReset = false;
}
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;
// 获取排好序的子View,越顶层的View越靠前
getTopSortedChildren(topmostChildList);
// 从顶层View向底层View进行迭代
final int childCount = topmostChildList.size();
for (int i = 0; i < childCount; i++) {
final View child = topmostChildList.get(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Behavior b = lp.getBehavior();
// 这里看不懂
if ((intercepted || newBlock) && action != MotionEvent.ACTION_DOWN) {
// Cancel all behaviors beneath the one that intercepted.
// 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);
}
switch (type) {
case TYPE_ON_INTERCEPT:
b.onInterceptTouchEvent(this, child, cancelEvent);
break;
case TYPE_ON_TOUCH:
b.onTouchEvent(this, child, cancelEvent);
break;
}
}
continue;
}
// 如果还没被处理且Behavior不为空,则调用Behavior的相关方法。
if (!intercepted && b != null) {
switch (type) {
case TYPE_ON_INTERCEPT:
intercepted = b.onInterceptTouchEvent(this, child, ev);
break;
case TYPE_ON_TOUCH:
intercepted = b.onTouchEvent(this, child, ev);
break;
}
// 如果Behavior处理了,则mBehaviorTouchView则为Behavior的对应View。
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;
}
private void getTopSortedChildren(List<View> out) {
out.clear();
// isChildrenDrawingOrderEnabled的返回值由setChildrenDrawingOrderEnabled设置。
final boolean useCustomOrder = isChildrenDrawingOrderEnabled();
final int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
// getChildDrawingOrder是获取View的绘制索引,默认是返回所给参数。
// 只有setChildrenDrawingOrderEnabled的参数为true,getChildDrawingOrder才会被调用。
final int childIndex = useCustomOrder ? getChildDrawingOrder(childCount, i) : i;
final View child = getChildAt(childIndex);
out.add(child);
}
// 当API >= 21时,TOP_SORTED_CHILDREN_COMPARATOR为ViewElevationComparator。
// ViewElevationComparator比较的是View的z坐标。
if (TOP_SORTED_CHILDREN_COMPARATOR != null) {
Collections.sort(out, TOP_SORTED_CHILDREN_COMPARATOR);
}
}
如果Behavior的blocksInteractionBelow方法返回true,则它下面的View将无法进行事件交互。为了显示无法进行交互的区域,会在Behavior对应的View下面绘制一层由getScrimColor和getScrimOpacity决定的颜色。
Behavior与WindowInsets
当CoordinatorLayout设置fitsSystemWindows为true时,当监听器OnApplyWindowInsetsListener监听到WindowInsets发生变化时,CoordinatorLayout会通过dispatchApplyWindowInsetsToBehaviors方法来回调Behavior的onApplyWindowInsets方法。