@RitcheeQinG
2020-04-26T02:55:34.000000Z
字数 9318
阅读 264
Android
过去自己记录的内容归档,内容较老,仅供参考
/**
* Measure specification mode: The parent has not imposed any constraint
* on the child. It can be whatever size it wants.
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
/**
* Measure specification mode: The parent has determined an exact size
* for the child. The child is going to be given those bounds regardless
* of how big it wants to be.
*/
public static final int EXACTLY = 1 << MODE_SHIFT;
/**
* Measure specification mode: The child can be as large as it wants up
* to the specified size.
*/
public static final int AT_MOST = 2 << MODE_SHIFT;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// ...
int myWidth = -1;
int myHeight = -1;
int width = 0;
int height = 0;
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// Record our dimensions if they are known;
// 如果是unspecified,那就是-1了,而这个-1会让其子view都是unspecified
if (widthMode != MeasureSpec.UNSPECIFIED) {
myWidth = widthSize;
}
if (heightMode != MeasureSpec.UNSPECIFIED) {
myHeight = heightSize;
}
if (widthMode == MeasureSpec.EXACTLY) {
width = myWidth;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = myHeight;
}
// ...
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// ...
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
int[] rules = params.getRules(layoutDirection);
// 基本上是在处理child和其周围相关child的关系,重新计算其layoutParams
applyHorizontalSizeRules(params, myWidth, rules);
measureChildHorizontal(child, params, myWidth, myHeight);
if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
offsetHorizontalAxis = true;
}
}
}
// ...
}
private void measureChildHorizontal(
View child, LayoutParams params, int myWidth, int myHeight) {
// myWidth和myHeight是RelativeLayout的,如果是unspecific,那就是-1
// params是child自己的
// widthMeasureSpec在这个下面这个方法里就算是计算完成了,但heightMeasureSpec还要在下面算
final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight, params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight, myWidth);
final int childHeightMeasureSpec;
if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
// relativeLayout is unspecified, and version > 17
if (params.height >= 0) {
// 众所周知wrap_content和match_parent分别是-2和-1
// 代表child有写死值的高度了
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
params.height, MeasureSpec.EXACTLY);
} else {
// parent都不确定,child如果是wrap或者match肯定也确定不了
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
} else {
// at most or exactly
final int maxHeight;
if (mMeasureVerticalWithPaddingMargin) {
// version >= 18
// 0 或者 RL高度 - (topPadding, topMargin, bottomPadding, bottomMargin)里面的最大值
maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom - params.topMargin - params.bottomMargin);
} else {
// 似乎不到18这里的maxHeight就不把padding margin算进去了
maxHeight = Math.max(0, myHeight);
}
final int heightMode;
// 意思是如果此RelativeLayout的值确定了,child就只有match_parent时能确认值就和parent一样,其他情况都要按at most判断(因为即使写死值也可能比parent大)
if (params.height == LayoutParams.MATCH_PARENT) {
heightMode = MeasureSpec.EXACTLY;
} else {
heightMode = MeasureSpec.AT_MOST;
}
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
总结一下这里的第一次绘制
width = getChildMeasureSpec, height = myHeight - padding - margin
/**
* @param childStart The left or top field of the child's layout params
* @param childEnd The right or bottom field of the child's layout params
* @param childSize The child's desired size (the width or height field of the child's layout params)
* @param startMargin The left or top margin
* @param endMargin The right or bottom margin
* @param startPadding mPaddingLeft or mPaddingTop
* @param endPadding mPaddingRight or mPaddingBottom
* @param mySize The width or height of this view (the RelativeLayout)
* @return MeasureSpec for the child
*/
private int getChildMeasureSpec(int childStart, int childEnd, int childSize, int startMargin, int endMargin, int startPadding, int endPadding, int mySize) {
int childSpecMode = 0;
int childSpecSize = 0;
final boolean isUnspecified = mySize < 0;
if (isUnspecified && !mAllowBrokenMeasureSpecs) {
// 先忽略unSpecified,总之child宽高只要有值就会是exactly
}
// Figure out start and end bounds.
// 这个方法里并不会再给childStart和childEnd赋值,如果没有任何约束,那默认自然是在左上角
int tempStart = childStart;
int tempEnd = childEnd;
// 从上面给params赋值的过程可以看到,这里的值如果是default value,存在几种情况
// 1.约束自己的对象gone掉了或者不存在,而且没设置alignParentIfMissing
// 2.压根就没有约束
// If the view did not express a layout constraint for an edge, use
// view's margins and our padding
if (tempStart == VALUE_NOT_SET) {
// 没有约束的情况下(在左上角),左边最大可以是padding + margin
tempStart = startPadding + startMargin;
}
if (tempEnd == VALUE_NOT_SET) {
// 没有约束的情况下,右边最大是RL的右边 - padding - margin
tempEnd = mySize - endPadding - endMargin;
}
// child的width最大的可能就是 mySize - padding - margin
// 那么假设margin < 0 ?
// Figure out maximum size available to this view
final int maxAvailable = tempEnd - tempStart;
if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
// Constraints fixed both edges, so child must be an exact size.
// 如果都有值了,就会是需求的right - left的精确范围
childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
childSpecSize = Math.max(0, maxAvailable);
} else {
// 有至少一边没定下来
if (childSize >= 0) {
// width/height >= 0
// Child wanted an exact size. Give as much as possible.
childSpecMode = MeasureSpec.EXACTLY;
if (maxAvailable >= 0) {
// We have a maximum size in this dimension.
childSpecSize = Math.min(maxAvailable, childSize);
} else {
// We can grow in this dimension.
childSpecSize = childSize;
}
} else if (childSize == LayoutParams.MATCH_PARENT) {
// Child wanted to be as big as possible. Give all available
// space.
childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
childSpecSize = Math.max(0, maxAvailable);
} else if (childSize == LayoutParams.WRAP_CONTENT) {
// Child wants to wrap content. Use AT_MOST to communicate
// available space if we know our max size.
if (maxAvailable >= 0) {
// We have a maximum size in this dimension.
childSpecMode = MeasureSpec.AT_MOST;
childSpecSize = maxAvailable;
} else {
// We can grow in this dimension. Child can be as big as it
// wants.
childSpecMode = MeasureSpec.UNSPECIFIED;
childSpecSize = 0;
}
}
}
return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
}
private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
RelativeLayout.LayoutParams anchorParams;
// VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
// left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
// wants to the right
// left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
// wants to the left
// left=10, right=20 means the left and right ends are both fixed
childParams.mLeft = VALUE_NOT_SET; // defualt value
childParams.mRight = VALUE_NOT_SET;
// anchor是对方的,child是自己的
// 这里的left of从下文来看应该是指child left of 对方
anchorParams = getRelatedViewParams(rules, LEFT_OF);
// anchorParams可能为空的情况:1.Gone 2.没有这个View
if (anchorParams != null) {
// 对方的右边 - 双方margin = child左边
childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin + childParams.rightMargin);
} else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
// layout_alignWithParentIfMissing + left of的view不存在or gone
/* alignWithParent: When true, uese the parent as the anchor if the anchor doesn't exist or if the anchor's visibility is gone */
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
// 如果啥都没标注,此时应该left还是not set
anchorParams = getRelatedViewParams(rules, RIGHT_OF);
if (anchorParams != null) {
childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin + childParams.leftMargin);
} else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
}
anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
if (anchorParams != null) {
// 对方的margin此时已经被加进left了吗?
childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
} else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
}
anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
if (anchorParams != null) {
childParams.mRight = anchorParams.mRight - childParams.rightMargin;
} else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
if (0 != rules[ALIGN_PARENT_LEFT]) {
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
}
if (0 != rules[ALIGN_PARENT_RIGHT]) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
// 判断的顺序: left of, right of, align left, align right, align parnent left, align parent left
// 如果rules中什么都不包括,那么到了这里应该还是default value
}
sort过程:
会按照依赖关系进行排序,需要先处理的会被排序在前面
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// ...
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 = 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;
}
}
}
}
}
// ...
}