1 measure原理
1.ViewRootImpl.performTraversals是系统进行view树遍历的核心函数,该函数内部会依次执行measure.layout.draw过程.
2.对于view的绘制,最后都是通过Canvas来实现的.而Canvas的大小是无限而.我们设定view的大小是为了在Canvas绘制时指定一个固定的区域来绘制某个view.
3.layout_width,layout_heigh,设置的值标识父视图给该view分配的窗口大小.而不是view的大小.
4.measure过程是从ViewRootImpl中调用host.measure()开始.然后调用到View.measure()函数View.measure函数被final修饰,无法被子类覆盖.表示系统不希望我们重写修改View.measure()函数View.measure()函数中会调用到onMeasure()函数.而host一般都是ViewGroup.并实现了onMeasure()函数.需要在ViewGroup.onMeasure()函数中完成对子视图的measure过程. 系统为ViewGroup提供了 measureChild,measureChildWithMargins方法来协助完成子视图的measure过程.
5.视图的最终测量.是调用 View.setMeasuredDimension(int measuredWidth, int measuredHeight)来完成.int参数由两部分组成. int的前两位表示测量方式(UNSPECIFIED,EXACTLY,AT_MOST) ,int 后30位 表示测量的值. 通过下边的方法拿到测量值和测量的方式.
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
6.对于最根上的host.measure的参数.他的测量值就是窗口本身的大小.他的测量方式是EXACTLY.视图的窗口大小是有由视图和他的父视图共同决定的.既每个view通过他自己的layout_height和他父视图Mode和Size的一起决定该视图的Mode和Size;
6.ViewGroup提供measureChildWithMargins,measureChildren,measureChild三个方法辅助测量子类.这三个方法本质一样.measureChildWithMargins 提供了对margin和padding的处理.接下来看ViewGroup.measureChildWithMargins方法
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//1.得到view的layoutparams
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);//2.得到子视图宽度的最终值(包含mode和size)
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);//3.得到子视图高度的最终值(包含mode和size)
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);//4.设置子视图的测量大小.
}
在第一步中.得到child的layoutParams并强转为MarginLayoutParams,是因为view.getLayoutParams()返回的LayoutParams参数是在view被
viewGroup调用addView()时为view添加的.添加的LayoutParams通常都是继承自MarginLayoutParams.
第四步是所有view的最终设置方法.
第二第三步是相同的.看第二步,主要操作就根据父视图的宽度mode和size及子视图的layout_parems(lp.width)来最终完成子视图的宽高.如下.
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
对应关系如下
2 LinearLayout的onMeasure原理
1.看measureVertical(int widthMeasureSpec, int heightMeasureSpec) 垂直方向的测量
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
//1.第一部分.计算所有视图的高度.
mTotalLength = 0;//总高度
float totalWeight = 0; /总weight
final int count = getVirtualChildCount();//子视图数量
final int widthMode = MeasureSpec.getMode(widthMeasureSpec); //父视图的宽的模式
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
totalWeight += lp.weight;
//这里一段是对含有layout_weight的view的处理.如果父视图的model是EXACTLY,那么当其他子视图占满父视图高度时,
wetght>0的子视图可能分配不到空间,对weight大于0的子视图跳过测量
if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
skippedMeasure = true;
} else {
int oldHeight = Integer.MIN_VALUE;
if (lp.height == 0 && lp.weight > 0) {
oldHeight = 0;
lp.height = LayoutParams.WRAP_CONTENT;
}
//调用measureChildWithMargins 对子视图进行测量.
measureChildBeforeLayout(
child, i, widthMeasureSpec, 0, heightMeasureSpec,
totalWeight == 0 ? mTotalLength : 0);
final int childHeight = child.getMeasuredHeight();
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
}
//对宽度的处理.
final int margin = lp.leftMargin + lp.rightMargin;
final int measuredWidth = child.getMeasuredWidth() + margin;
maxWidth = Math.max(maxWidth, measuredWidth);
childState = combineMeasuredStates(childState, child.getMeasuredState());
allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
if (lp.weight > 0) {
weightedMaxWidth = Math.max(weightedMaxWidth,
matchWidthLocally ? margin : measuredWidth);
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth,
matchWidthLocally ? margin : measuredWidth);
}
i += getChildrenSkipCount(child, i);
}
if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
mTotalLength += mDividerHeight;
}
//这段忽略.
if (useLargestChild &&
(heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}
if (child.getVisibility() == GONE) {
i += getChildrenSkipCount(child, i);
continue;
}
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
// Account for negative margins
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}
}
//对高度的进一步计算,这里判断父视图还有没有空间,把剩余空间分配给weight>0的子视图,这里会测量weight>0的子视图
mTotalLength += mPaddingTop + mPaddingBottom;
int heightSize = mTotalLength;
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());\
//这个方法获得子视图最终能占用的布局大小.
int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
//heightSize 表示父视图能提供的并且子视图肯定会占用完的高度,
//mTotalLength表示所有子视图的高度
//两者的差 delta如果大于0,表示有剩余空间,会分配给weight>0的子视图,如果小delta<0,标识空间不够,则需要weight>0的子视图腾出空间.
int delta = heightSize - mTotalLength;
if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
float childExtra = lp.weight;
if (childExtra > 0) {
// share 就是根据weight 额外分配的空间,可能是负值,正值表示增加,负值标识减少.
int share = (int) (childExtra * delta / weightSum);
weightSum -= childExtra;
delta -= share;
final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
mPaddingLeft + mPaddingRight +
lp.leftMargin + lp.rightMargin, lp.width);
if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
int childHeight = child.getMeasuredHeight() + share;//将额外的空间交给子视图
if (childHeight < 0) {
childHeight = 0;
}
child.measure(childWidthMeasureSpec,
MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
} else {
child.measure(childWidthMeasureSpec,
MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
MeasureSpec.EXACTLY));
}
}
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}
mTotalLength += mPaddingTop + mPaddingBottom;
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth,
weightedMaxWidth);
/ /再一次进行测量.在第一次measure过的就不需要测量.对有所weight>0的再次进行分配.--这段我也没太懂.
if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();
float childExtra = lp.weight;
if (childExtra > 0) {
child.measure(
MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(largestChildHeight,
MeasureSpec.EXACTLY));
}
}
}
}
if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
maxWidth = alternativeMaxWidth;
}
maxWidth += mPaddingLeft + mPaddingRight;
// Check against our minimum width
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
heightSizeAndState);
}
``` measures 结束.