measureChildWithMargins
代码如下
-
protected void measureChildWithMargins(View child, -
int parentWidthMeasureSpec, int widthUsed, -
int parentHeightMeasureSpec, int heightUsed) { -
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); -
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, -
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin -
+ widthUsed, lp.width); -
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, -
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin -
+ heightUsed, lp.height); -
child.measure(childWidthMeasureSpec, childHeightMeasureSpec); -
}
主要调用了getChildMeasureSpec()方法
getChildMeasureSpec
代码如下
-
public static int getChildMeasureSpec(int spec, int padding, int childDimension) { -
// 以宽度为例,传入的参数分别是:父view的widthMeasureSpec、已用宽度(子view左右内外间距+已用的间距)、子view要求的宽度 -
int specMode = MeasureSpec.getMode(spec); -
int specSize = MeasureSpec.getSize(spec); -
int size = Math.max(0, specSize - padding); // 父宽度 - 子view的内外间距 -
// 也就是子view的最大宽度 -
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. sdk < 23时,sUseZeroUnspecifiedMeasureSpec为true -
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; -
resultMode = MeasureSpec.UNSPECIFIED; -
} else if (childDimension == LayoutParams.WRAP_CONTENT) { -
// Child wants to determine its own size.... find out how -
// big it should be. -
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; -
resultMode = MeasureSpec.UNSPECIFIED; -
} -
break; -
} -
//noinspection ResourceType -
return MeasureSpec.makeMeasureSpec(resultSize, resultMode); -
}
combineMeasuredState
-
public static int combineMeasuredStates(int curState, int newState) { -
return curState | newState; -
}
没啥好说的,合并两个state
getMeasuredState
-
public final int getMeasuredState() { -
return (mMeasuredWidth&MEASURED_STATE_MASK) -
| ((mMeasuredHeight>>MEASURED_HEIGHT_STATE_SHIFT) -
& (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT)); -
}
通过位运算合并高度和宽度的state,结果就是第1个字节是宽度的state,第3个字节是高度的state
resolveSizeAndState
-
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { // size是自己需要的尺寸,measureSpec是父view指定的尺寸 -
final int specMode = MeasureSpec.getMode(measureSpec); -
final int specSize = MeasureSpec.getSize(measureSpec); // 父view指定的尺寸 -
final int result; -
switch (specMode) { -
case MeasureSpec.AT_MOST: -
if (specSize < size) { -
result = specSize | MEASURED_STATE_TOO_SMALL; // 设定标志位,表示父view得分配的再大一些 -
} else { -
result = size; -
} -
break; -
case MeasureSpec.EXACTLY: -
result = specSize; -
break; -
case MeasureSpec.UNSPECIFIED: -
default: -
result = size; -
} -
return result | (childMeasuredState & MEASURED_STATE_MASK); -
}
最后保存状态时,由于childMeasureState是第一个字节是宽度的状态,第三个字节是高度的状态,所以再测量宽度高度时调用此方法,要对childState进行位运算
setMeasuredDimension
这个是用来保存当前view尺寸的,当我们使用自定义view并且覆写了onMeasure()方法时,不在最后调用这个方法,直接就报错了
-
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { -
boolean optical = isLayoutModeOptical(this); -
if (optical != isLayoutModeOptical(mParent)) { // layout_mode是LAYOUT_MODE_OPTICAL的情况很少出现,所以测量过程中涉及optical的,我们一般都可以直接忽略不计 -
Insets insets = getOpticalInsets(); -
int opticalWidth = insets.left + insets.right; -
int opticalHeight = insets.top + insets.bottom; -
measuredWidth += optical ? opticalWidth : -opticalWidth; -
measuredHeight += optical ? opticalHeight : -opticalHeight; -
} -
setMeasuredDimensionRaw(measuredWidth, measuredHeight); -
}
直接调用了setMeasuredDimensionRaw()方法
-
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) { -
mMeasuredWidth = measuredWidth; -
mMeasuredHeight = measuredHeight; -
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; -
}
嗯,就是这一步,保存了measuredHeight和measuredWidth.不过,这里要说明一下,measuredHeight/Width高八位是状态,低二十四位才是真正的尺寸,这也就是View.getMeasuredWidth/Height和View.getMeasuredWidth/HeightAndState()方法的区别所在
getDefaultSize
-
public static int getDefaultSize(int size, int measureSpec) { -
int result = size; // size是view的默认尺寸 -
int specMode = MeasureSpec.getMode(measureSpec); -
int specSize = MeasureSpec.getSize(measureSpec); -
switch (specMode) { -
case MeasureSpec.UNSPECIFIED: -
result = size; -
break; -
case MeasureSpec.AT_MOST: -
case MeasureSpec.EXACTLY: -
result = specSize; -
break; -
} -
return result; -
}
getSuggestMinimunHeight/Width
以高度为例,代码如下
-
protected int getSuggestedMinimumHeight() { -
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight()); -
}
涉及view的背景,mBackground是Drawable类的,它的getMinimumHeight()由不同的子类分别实现,反正就是返回最小的高度,然后再跟view本身的最小高度取最大值(view本身的最小高度不手动设置的话,就是0)
本文深入解析了Android中View的测量流程,重点介绍了measureChildWithMargins、getChildMeasureSpec等关键方法的作用及其实现细节,并解释了如何通过这些方法正确测量View的尺寸。
1145

被折叠的 条评论
为什么被折叠?



