CoordinatorLayout解析子View依赖关系

CoordinatorLayout的LayoutParams

LayoutParams保存了CoordinatorLayout子View的一些重要信息:

  1. Behavior mBehavior:该View指定的Behavior。
  2. boolean mBehaviorResolved:是否解析出Behavior。
  3. int mAnchorId:XML中layout_anchor属性值。
  4. View mAnchorView:mAnchorId对应的View。
  5. 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);
    }
}
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值