CoordinatorLayout的LayoutParams
LayoutParams保存了CoordinatorLayout子View的一些重要信息:
- Behavior mBehavior:该View指定的Behavior。
- boolean mBehaviorResolved:是否解析出Behavior。
- int mAnchorId:XML中layout_anchor属性值。
- View mAnchorView:mAnchorId对应的View。
- View mAnchorDirectChild:mAnchorView向上查找到的CoordinatorLayout的直接子View。
CoordinatorLayout解析子View间的依赖关系
CoordinatorLayout在测量时会调用prepareChildren方法来解析子View间的依赖关系。
// API 25.0.1的源码
private void prepareChildren() {
// mDependencySortedChildren是一个List,用来保存根据依赖关系排序后的View,被依赖的排在前面。
mDependencySortedChildren.clear();
// mChildDag是一个自定义的数据结构,有向无环图,用来保存子View之间的依赖关系
mChildDag.clear();
// 遍历所有的子View
for (int i = 0, count = getChildCount(); i < count; i++) {
final View view = getChildAt(i);
final LayoutParams lp = getResolvedLayoutParams(view);
lp.findAnchorView(this, view);
// 把View加入图中
mChildDag.addNode(view);
// 检查其他View和当前View是否有依赖关系
for (int j = 0; j < count; j++) {
if (j == i) {
continue;
}
final View other = getChildAt(j);
final LayoutParams otherLp = getResolvedLayoutParams(other);
// 如果通过XML的layout_anchor属性或者Behavior的layoutDependsOn方法指定了依赖关系
if (otherLp.dependsOn(this, other, view)) {
if (!mChildDag.contains(other)) {
// 如果依赖的View还没加入图中,则加入
mChildDag.addNode(other);
}
// 记录依赖关系
mChildDag.addEdge(view, other);
}
}
}
// 把mChildDag的排序结果保存到mDependencySortedChildren中
mDependencySortedChildren.addAll(mChildDag.getSortedList());
// 反向排序,把不依赖其他View的子View放在前面。
Collections.reverse(mDependencySortedChildren);
}
复制代码
先来看看getResolvedLayoutParams方法:
// 该方法主要是解析是否指定了Behavior
LayoutParams getResolvedLayoutParams(View child) {
final LayoutParams result = (LayoutParams) child.getLayoutParams();
//如果在XML中指定了app:layout_behavior或者调用了setBehavior方法,那么mBehaviorResolved为true。
//如果mBehaviorResolved为false,则查看是否有DefaultBehavior注解。
if (!result.mBehaviorResolved) {
Class<?> childClass = child.getClass();
DefaultBehavior defaultBehavior = null;
//检查该View类是否有DefaultBehavior注解,没有则查看父类。
//这个说明父类的Behavior在子类没有Behavior的情况下是会被继承的。
while (childClass != null &&
(defaultBehavior = childClass.getAnnotation(DefaultBehavior.class)) == null) {
childClass = childClass.getSuperclass();
}
//找到DefaultBehavior,则调用Behavior的无参数构造方法,保持到LayoutParams中并设置mBehaviorResolved为true。
if (defaultBehavior != null) {
try {
result.setBehavior(defaultBehavior.value().newInstance());
} catch (Exception e) {
Log.e(TAG, "Default behavior class " + defaultBehavior.value().getName() +
" could not be instantiated. Did you forget a default constructor?", e);
}
}
result.mBehaviorResolved = true;
}
return result;
}
复制代码
再看看findAnchorView方法的resolveAnchorView方法:
//该方法通过XML中指定的layout_anchor属性来查找锚点View。
//这段代码说明为View指定的anchor不能是父View CoordinatorLayout,也不能是该View的子View
private void resolveAnchorView(final View forChild, final CoordinatorLayout parent) {
mAnchorView = parent.findViewById(mAnchorId);
if (mAnchorView != null) {
//如果AnchorView是父View CoordinatorLayout则抛出异常。
if (mAnchorView == parent) {
if (parent.isInEditMode()) {
mAnchorView = mAnchorDirectChild = null;
return;
}
throw new IllegalStateException(
"View can not be anchored to the the parent CoordinatorLayout");
}
//从mAnchorView沿着View树向上迭代,如果mAnchorView是View的子View则抛出异常
View directChild = mAnchorView;
for (ViewParent p = mAnchorView.getParent();
p != parent && p != null;
p = p.getParent()) {
if (p == forChild) {
if (parent.isInEditMode()) {
mAnchorView = mAnchorDirectChild = null;
return;
}
throw new IllegalStateException(
"Anchor must not be a descendant of the anchored view");
}
if (p instanceof View) {
directChild = (View) p;
}
}
//mAnchorDirectChild是mAnchorView的父View也是CoordinatorLayout的直接子View。
mAnchorDirectChild = directChild;
} else {
if (parent.isInEditMode()) {
mAnchorView = mAnchorDirectChild = null;
return;
}
throw new IllegalStateException("Could not find CoordinatorLayout descendant view"
+ " with id " + parent.getResources().getResourceName(mAnchorId)
+ " to anchor view " + forChild);
}
}
复制代码