- 目录
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);
}
}
}