Behavior和View依赖
可以通过XML的layout_anchor属性和Behavior的layoutDependsOn()方法两种方式来指定CoordinatorLayout的子View间的依赖关系。
当被依赖View被删除时,Behavior的onDependentViewRemoved()方法会被回调。当被依赖View的布局发生变化时,Behavior的onDependentViewChanged()方法会被回调。
CoordinatorLayout通过onChildViewsChanged方法来实现Behavior的方法回调。该方法的参数type有3种值,EVENT_NESTED_SCROLL、EVENT_VIEW_REMOVED、EVENT_PRE_DRAW,分别对应3中不同的情形。
在Behavior的嵌套滚动方法中,会在滚动后调用onChildViewsChanged方法,type为EVENT_NESTED_SCROLL。
CoordinatorLayout在构造函数中注册了OnHierarchyChangeListener监听器。
super.setOnHierarchyChangeListener(new HierarchyChangeListener());
@Override
public void setOnHierarchyChangeListener(OnHierarchyChangeListener onHierarchyChangeListener) {
mOnHierarchyChangeListener = onHierarchyChangeListener;
}
复制代码
HierarchyChangeListener在View被移除时,调用onChildViewsChanged方法,type为EVENT_VIEW_REMOVED。
private class HierarchyChangeListener implements OnHierarchyChangeListener {
HierarchyChangeListener() {
}
@Override
public void onChildViewAdded(View parent, View child) {
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
}
}
@Override
public void onChildViewRemoved(View parent, View child) {
onChildViewsChanged(EVENT_VIEW_REMOVED);
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
}
}
}
复制代码
Coordinator在onMeasure方法中通过ensurePreDrawListener方法注册了ViewTreeObserver.OnPreDrawListener 监听器。在每次绘制View前调用调用onChildViewsChanged方法,type为EVENT_PRE_DRAW。
class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
@Override
public boolean onPreDraw() {
onChildViewsChanged(EVENT_PRE_DRAW);
return true;
}
}
复制代码
onChildViewsChanged方法会根据锚点的位置和Insets调整View的位置,然后再回调Behavior的方法。
final void onChildViewsChanged(@DispatchChangeEvent final int type) {
final int layoutDirection = ViewCompat.getLayoutDirection(this);
final int childCount = mDependencySortedChildren.size();
final Rect inset = mTempRect4;
inset.setEmpty();
// 遍历根据依赖关系排好序的View
for (int i = 0; i < childCount; i++) {
final View child = mDependencySortedChildren.get(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
// 寻找排在前面的View是否有锚点
for (int j = 0; j < i; j++) {
final View checkChild = mDependencySortedChildren.get(j);
// 如果有锚点
if (lp.mAnchorDirectChild == checkChild) {
// offsetChildToAnchor方法会根据锚点调整子View的位置,并回调onDependentViewChanged方法。
offsetChildToAnchor(child, layoutDirection);
}
}
final Rect drawRect = mTempRect1;
getChildRect(child, true, drawRect);
if (lp.insetEdge != Gravity.NO_GRAVITY && !drawRect.isEmpty()) {
final int absInsetEdge = GravityCompat.getAbsoluteGravity(
lp.insetEdge, layoutDirection);
switch (absInsetEdge & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
inset.top = Math.max(inset.top, drawRect.bottom);
break;
case Gravity.BOTTOM:
inset.bottom = Math.max(inset.bottom, getHeight() - drawRect.top);
break;
}
switch (absInsetEdge & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
inset.left = Math.max(inset.left, drawRect.right);
break;
case Gravity.RIGHT:
inset.right = Math.max(inset.right, getWidth() - drawRect.left);
break;
}
}
if (lp.dodgeInsetEdges != Gravity.NO_GRAVITY && child.getVisibility() == View.VISIBLE) {
// 调整子View的Inset
offsetChildByInset(child, inset, layoutDirection);
}
if (type == EVENT_PRE_DRAW) {
final Rect lastDrawRect = mTempRect2;
getLastChildRect(child, lastDrawRect);
// 如果布局没有变化,则遍历下一个
if (lastDrawRect.equals(drawRect)) {
continue;
}
// 如果变化了,要记录上一次的位置。
recordLastChildRect(child, drawRect);
}
// 遍历排在后面的View,看是否依赖当前的子View。
for (int j = i + 1; j < childCount; j++) {
final View checkChild = mDependencySortedChildren.get(j);
final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
final Behavior b = checkLp.getBehavior();
// 如果Behavior的layoutDependsOn返回true。
if (b != null && b.layoutDependsOn(this, checkChild, child)) {
if (type == EVENT_PRE_DRAW && checkLp.getChangedAfterNestedScroll()) {
// 如果是EVENT_PRE_DRAW但已经在EVENT_NESTED_SCROLL中处理过了,则遍历下一个。
checkLp.resetChangedAfterNestedScroll();
continue;
}
final boolean handled;
switch (type) {
case EVENT_VIEW_REMOVED:
// 如果是移除事件则调用onDependentViewRemoved方法。
b.onDependentViewRemoved(this, checkChild, child);
handled = true;
break;
default:
// 如果是EVENT_NESTED_SCROLL或者EVENT_PRE_DRAW,调用onDependentViewChanged方法。
handled = b.onDependentViewChanged(this, checkChild, child);
break;
}
if (type == EVENT_NESTED_SCROLL) {
// 如果是EVENT_NESTED_SCROLL,则记录onDependentViewChanged的处理结果
checkLp.setChangedAfterNestedScroll(handled);
}
}
}
}
}
复制代码