首先来看measure方法的源码注解:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//判断旧的mOldWidthMeasureSpec 是否与新的测量方式相同,只要有一个不同就要重新测量
//对widthMeasureSpec的理解:
if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
// first clears the measured dimension flag
//清除标志位,"先取反,后与" :与是为了取被与的值
mPrivateFlags &= ~MEASURED_DIMENSION_SET;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
}
// measure ourselves, this should set the measured dimension flag back
//开始测量
onMeasure(widthMeasureSpec, heightMeasureSpec);
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
//通过标志位判断是否执行了setMeasuredDimension()方法
if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= LAYOUT_REQUIRED;
}
//将重新为旧的值赋值
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}
measure函数有2个参数,widthMeasureSpec 和 heightMeasureSpec。
onMeasure(widthMeasureSpec, heightMeasureSpec)方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//执行setMeasuredDimension方法
//参数解释:
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
当我们重写onMeasure时,需要调用setMeasuredDimension来设置自身的mMeasuredWidth和mMeasuredHeight,否则,就会抛出上面那个异常
setMeasuredDimension方法:类似于执行setter方法
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
//将测量结果赋值
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
//添加flag,MEASURED_DIMENSION_SET
mPrivateFlags |= MEASURED_DIMENSION_SET;
}
计算都是在getDefaultSize函数里实现的:
//参数解析:size:大小
//measureSpec:是一个复合的标志位
public static int getDefaultSize(int size, int measureSpec) {
//measureSpec是一个复合的概念:二进制表示:最前的2两位表示:mode;后30位表示尺寸
//关于mode:MeasureSpec.UNSPECIFIED这个是未执行大小:对应于wrap_content
//MeasureSpec.AT_MOST:对应match_parent和fill_parent
//MeasureSpec.EXACTLY:代表确切尺寸
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
//未指定大小的情况下:返回建议的尺寸
case MeasureSpec.UNSPECIFIED:
result = size;
break;
//AT_MOST,EXACTLY则返回按照mode测量的尺寸.
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
关于getSuggestedMinimumHeight()
protected int getSuggestedMinimumHeight() {
//判断是否有背景,没有的话返回mMinHeight 即控件的最小高度(通过属性获取的)
//如果背景不为空,则返回mMinHeight 或mBackground.getMinimumHeight()
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
public static class MeasureSpec {
//设置mode的移位数
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
//UNSPECIFIED为32个0
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
//EXACTLY为:01+30个0
public static final int EXACTLY = 1 << MODE_SHIFT;
//AT_MOST为:10+30个0
public static final int AT_MOST = 2 << MODE_SHIFT;
public static int makeMeasureSpec(int size, int mode) {
//组建复合的measureSpec
return size + mode;
}
public static int getMode(int measureSpec) {
//取出mode
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
//去除mode,获取size
return (measureSpec & ~MODE_MASK);
}
}
测量总结:
measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED)即measure(0,0)返回getSuggestedMinimumWidth()
如果采用MeasureSpec.AT_MOST或者
则舍弃getSuggestedMinimumWidth()得到的尺寸,返回int specSize =MeasureSpec.getSize(measureSpec);的值MeasureSpec.EXACTLY;这是传入measure()的measureSpec参数,是mode+size,因此采用此两种测量方式需要制定尺寸.