返回介绍

2.2 Layout

发布于 2024-12-23 22:19:58 字数 3509 浏览 0 评论 0 收藏 0

onLayout 中,我们可以看到 CoordinatorLayout 会对每一个子 View 依照以下判断顺序进行 layout:

  1. 如果子 View 设置了 Behavior ,并且该 Behaviorbehavior.onLayoutChild 返回 true ,则使用 behavior.onLayoutChild 对该子 View 进行 layout;
  2. 如果 Behavior 不进行 layout,则进入自身的 onLayoutChild() ,内部依次进行如下判断:
  • 如果子 View 设置了 Anchor ,则调用 layoutChildWithAnchor (根据 anchor 进行 layout);
  • 如果子 View 含有 keyline ,则调用 layoutChildWithKeyline (根据 keyline 进行 layout);
  • 如果以上判断都不符合,则直接将 View 根据 padding/margin/measure 结果 按照 Gravity 放置。

我们一一来看一下这些过程。

2.2.1 使用 Behavior 进行 layout

默认的 Behavior 的 onLayoutChild 都是返回 false 的,那么我们看看 FloatingActionButton 的默认 Behavior 是怎么处理的吧:

 @Override
 public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,
     int layoutDirection) {

   // 检查该 FAB 是否依赖 AppBarLayout
   final List<View> dependencies = parent.getDependencies(child);
   for (int i = 0, count = dependencies.size(); i < count; i) {
     final View dependency = dependencies.get(i);
     if (dependency instanceof AppBarLayout
         && updateFabVisibility(parent, (AppBarLayout) dependency, child)) {
       break;
     }
   }

   // 调用 CoordinatorLayout 的 onLayoutChild 对 FAB 进行 layout
   parent.onLayoutChild(child, layoutDirection);
   // 在 API < 21 时,需要手动 offset 来让出阴影的位置
   offsetIfNeeded(parent, child);
   return true;
 }

这里主要是处理了如果 FAB 设置了 AppBarLayout 为 anchor 时(此时会对 AppBarLayout 有依赖),则当 AppBarLayout 的高度不足以显示 FAB 时将其隐藏)。

之后它会手动调用 CoordinatorLayout 自身的 onLayoutChild 方法进行 layout,即上述判断的第二步,那我们继续往下看。

2.2.2 使用 Anchor 进行 layout

如果 View 设置了 anchor,那么都会调用 layoutWithAnchor 进行 layout,代码与解释如下:

private void layoutChildWithAnchor(View child, View anchor, int layoutDirection) {
  final LayoutParams lp = (LayoutParams) child.getLayoutParams();

  final Rect anchorRect = mTempRect1;
  final Rect childRect = mTempRect2;

  /* 1. 找到被 anchor 的 View 的布局边界 */
  getDescendantRect(anchor, anchorRect);

  /* 2. 获取到被 anchor 的 View 布局边界之后,配合 layout_anchorGravity 与自身的 gravity 获取到最终要 layout 到的边界 */
  getDesiredAnchoredChildRect(child, layoutDirection, anchorRect, childRect);
  child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
}

这里用到的两个关键函数就是 getDescendantRectgetDesiredAnchoredChildRect ,它们的目的在我添加的注释中进行了解释,为保证文章的可读性就不再把代码放上来了,有兴趣的同学可以再自己去挖掘相应代码~~

2.2.3 直接 layout

如果之前的都不符合,就会走到这一步,我们看看它是怎么 layout 的:

private void layoutChild(View child, int layoutDirection) {
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    final Rect parent = mTempRect1;
    parent.set(getPaddingLeft()  lp.leftMargin,
        getPaddingTop()  lp.topMargin,
        getWidth() - getPaddingRight() - lp.rightMargin,
        getHeight() - getPaddingBottom() - lp.bottomMargin);

    //...(省略代码) 处理由于 fitsSystemWindows 带来的 inset

 	 // 按照 Gravity 与 measure 尺寸在父控件里面找到自己的位置,并进行 layout。
    final Rect out = mTempRect2;
    GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
        child.getMeasuredHeight(), parent, out, layoutDirection);
    child.layout(out.left, out.top, out.right, out.bottom);
  }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文