Android -容器- RelativeLayout

  • 目录
    Android -容器- FrameLayout
    Android -容器- LinearLayout
    Android -容器- RelativeLayout

参考: https://blog.csdn.net/wz249863091/article/details/51757069

onMeasure 流程

1. 先把内部子view根据纵向关系和横向关系排序

根据findRoots()方法的分析:
dependents 存的是依赖别人的node,如果A,C依赖B,那么B的的 dependents 里存的是A,C。
dependencies 存的是被依赖node的规则和node,如果A依赖B和D才能确定位置,那么 dependencies 里存的是 B,D。

移除当前node和dependencies的依赖关系,如果dependencies只和当前的node有依赖那么,移除之后 dependencies node也视为rootNode。如果B只被A依赖,那么移除和A的关系之后,B就不被任何Node依赖。如果node里存的依赖关系为0,即该view不依赖任何view,那么该view视为rootView

举个列子:A依赖B,B依赖C

首先存入Root的是C,因为他没有不依赖任何节点。
然后B在getSortedViews()方法中找到了依赖自己的A,并且解除了依赖关系,所以B也为rootNode,存入roots。
最后A也为rootNode,所以roots里的顺序为C,B,A。

2. 初始化变量值

在计算子分配子view的坐标时候,需要用到父view的尺寸,但是在完成下面这个操作之前,我们是无法拿到一个准确值,所以我们先用一个默认值代替,在计算完之后,我们再用偏移量去更新真实坐标,偏移量的计算公式是offset = DEFAULT_WIDTH - width。【应用与布局是:isLayoutRtl,即right to left】

3. 遍历水平关系的view

1)根据方向获得子view中设置的规则:
int[] rules = params.getRules(layoutDirection)
2)把获得的左右方向规则转化成左右坐标:
applyHorizontalSizeRules(params, myWidth, rules),确定childParams.mLeft 和 childParams.mRight 的数据。
3)测算出水平方向的子view尺寸:
measureChildHorizontal(child, params, myWidth, myHeight)
(1)如果在步骤2)中params.mLeft 和 params.mRight都设置过值,则childSpecSize = maxAvailable = mRight - mLeft;
(2)childSize >= 0,如为具体值200dp,则childSpecSize = Math.min(maxAvailable, childSize);
(3)childSize == LayoutParams.MATCH_PARENT,则childSpecSize = Math.max(0, maxAvailable);
(4)childSize == LayoutParams.WRAP_CONTENT,则childSpecSize = maxAvailable;

最终调用child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

4)据大小确定把子view放在父容器的哪个位置
positionChildHorizontal(child, params, myWidth, isWrapContentWidth)
当步骤2)中,childParams.mLeft 和 childParams.mRight 只能确定一个,或都不能确定时,此处,利用3) 中得到的view宽度,将childParams.mLeft 和 childParams.mRight 全部确定。如:params.mLeft = params.mRight - child.getMeasuredWidth();
需要区分rtl,还是ltr。

4. 遍历竖直关系的view

跟水平方向类似,由于水平方向已经遍历过,所以在竖直遍历过程中,能够确定该RelativeLayout的width、height、left、top、right、bottom。

if (isWrapContentWidth) {
  if (isLayoutRtl()) {
    if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
      width = Math.max(width, myWidth - params.mLeft);
    } else {
      width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
    }
  } else {
    if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
      width = Math.max(width, params.mRight);
    } else {
      width = Math.max(width, params.mRight + params.rightMargin);
    }
  }
}

if (isWrapContentHeight) {
  if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
    height = Math.max(height, params.mBottom);
  } else {
    height = Math.max(height, params.mBottom + params.bottomMargin);
  }
}

if (child != ignore || verticalGravity) {
  left = Math.min(left, params.mLeft - params.leftMargin);
  top = Math.min(top, params.mTop - params.topMargin);
}

if (child != ignore || horizontalGravity) {
  right = Math.max(right, params.mRight + params.rightMargin);
  bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
}

5. baseline计算
6. 宽度和高度修正

在步骤2中提到的,在计算完之后,我们再用偏移量去更新真实坐标,偏移量的计算公式是offset = DEFAULT_WIDTH - width。其中,offsetHorizontalAxis 只有在子view为CENTER_IN_PARENT 或 CENTER_HORIZONTAL 或 ALIGN_PARENT_END时为true。

这里就是根据得到准确的宽度和高度之后,对一些依赖父容器决定位置的子view重新做一次测量。

if (isWrapContentWidth) {
  // Width already has left padding in it since it was calculated by looking at
  // the right of each child view
  width += mPaddingRight;

  if (mLayoutParams != null && mLayoutParams.width >= 0) {
    width = Math.max(width, mLayoutParams.width);
  }

  width = Math.max(width, getSuggestedMinimumWidth()); //背景width
  width = resolveSize(width, widthMeasureSpec);

  if (offsetHorizontalAxis) {
    for (int i = 0; i < count; i++) {
      final View child = views[i];
      if (child.getVisibility() != GONE) {
        final LayoutParams params = (LayoutParams) child.getLayoutParams();
        final int[] rules = params.getRules(layoutDirection);
        if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
          centerHorizontal(child, params, width);
        } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
          final int childWidth = child.getMeasuredWidth();
          params.mLeft = width - mPaddingRight - childWidth;
          params.mRight = params.mLeft + childWidth;
        }
      }
    }
  }
}
7. setMeasuredDimension(width, height);保存宽高数据

onLayout 流程

实际上在onMeasure已经执行了布局并缓存了位置,直接将缓存的值应用于子项即可。

protected void onLayout(boolean changed, int l, int t, int r, int b) {
  //  The layout has actually already been performed and the positions
  //  cached.  Apply the cached values to the children.
  final int count = getChildCount();

  for (int i = 0; i < count; i++) {
    View child = getChildAt(i);
    if (child.getVisibility() != GONE) {
      RelativeLayout.LayoutParams st =
          (RelativeLayout.LayoutParams) child.getLayoutParams();
      child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
    }
  }
}

你可能感兴趣的:(Android -容器- RelativeLayout)