@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d(TAG, "onMeasure: ");
}
@Override
public void layout(@Px int l, @Px int t, @Px int r, @Px int b) {
super.layout(l, t, r, b);
Log.d(TAG, "layout: ");
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
Log.d(TAG, "draw: ");
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
返回一个默认的size:宽度或者是高度。如果测量规范中没有对子view的大小进行限制的话,子view的大小使用该返回值。也有可能返回更大的值
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
//获取到测量规范对象中的size和mode
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
//如果mode属于UNSPECIFIED即父view未对子view的大小做任何要求,则将默认的size返回给view
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
//父view对子view的测量进行了限制,则所返回的值就是测量规范中size的值,
//即父veiw给子view规定的值
result = specSize;
break;
}
return result;
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
getMinimumWidth():drawable的最小宽度
该方法返回的是所建议的view应该使用的最小宽度(在view的最小宽度和背景的最小宽度两个值中取最大值返回)
widthMeasureSpec宽度测量规范:父view给子view规定的水平方向上的测量规范,测量规范会包含一个mode和一个size
在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。
EX:当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
2.最大模式(MeasureSpec.AT_MOST)
这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。
EX:当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
3.未指定模式(MeasureSpec.UNSPECIFIED)
//这个是由我们给出的尺寸大小和模式生成一个包含这两个信息的int变量,这里这个模式这个参数,传三个常量中的一个。
public static int makeMeasureSpec(int size, int mode)
//这个是得到这个变量中表示的模式信息,将得到的值与三个常量进行比较。
public static int getMode(int measureSpec)
//这个是得到这个变量中表示的尺寸大小的值。
public static int getSize(int measureSpec)
把
//把这个变量里面的模式和大小组成字符串返回来,方便打日志
public static String toString(int measureSpec)
getDefaultSize方法总结:
也就是说,该方法返回了view的默认大小的值,这个值跟父view对子view是否进行了限制有关,
如果父view对子view没进行限制,则返回所建议的view的大小,
若进行了限制,则返回测量规范对象中的size。
最后回到setMeasuredDimension方法
方法介绍:
在调用onMeasure方法时必须调用该方法,来保存view所测量的宽和高,如果调用失败则会触发异常
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
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);
}
方法参数:
measuredWidth:view的测量的宽
measuredHeight:view的测量的高
这里有调用了一个方法 setMeasuredDimensionRaw(int measuredWidth, int measuredHeight)
作用就是保存所测量的宽和高的值,并且设置标志位
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
2>,约定:在覆写onMeasure方法时必须调用setMeasuredDimension方法来存储所测量的宽高值,如果存储失败会触发measure抛出 的illegalStateException异常,调用父类的onMesure方法也可以避免这个异常。(笔者注:也就是说要么调用父类的onMesure方法,要么自己手动在子view的onMesure方法中调用setMearsuredDimension方法,否则会抛出异常)
3>,如果在测量规范中没有规定更大的值那么基类中的测量值默认是background的大小,建议view的子类覆写onMeasure方法来进行更好的测量、
4>,如果子类覆写了该方法,那么测量view大小的任务就交给子类了,所测量的宽高不能小于view本身提供了一组view的最小值,可以通过getSuggestedMinimumHeight()/width()获取,获取到的是一组默认的没有padding的一组wrap_content的宽高