Android开发学习之View测量的内置常用方法
16lz
2021-01-26
背景
在阅读安卓各种view的onMeasure()方法时,会遇到一些被共同调用的方法,这里我做一个整理,以备来日查看
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(安卓)Studio中JNI使用的一些出现的错误及方法
- AppCompatActivity与toolbar的结合
- Android基础知识】选项菜单、上下文菜单、子菜单的使用
- PowerManager源码
- Android(安卓)面试
- Android(安卓)Media player 报错Error(38,0)
- android view(2) Activity、Window、DecorView
- Android(安卓)SeLinux权限问题和解决方法
- Android(安卓)SharedPreference源码浅析