MeasureSpec是View的内部类。下面看看MeasureSpec的官方定义。
MeasureSpec把从父布局传到子布局的参数封装到布局中。每一个MeasureSpec对象代表着高或者宽。MeasureSpec由大小和模式组成,是一个32位的int值,最高2位代表着SpecMode(测量模式),最低30位代表着SpecSize(规格大小)。SpecMode有三种模式,分别是
UNSPECIFIED //父布局没有强加子布局的大小约束,所以子布局可以有任意的大小
EXACTLY //父布局设定了子布局的大小,子布局不能超过这个大小
AT_MOST //子布局可以用任意具体值来设定其大小
现在看看View的工作流程
View的工作流程总结为三步
- measure 测量
- layout 布局
- draw 绘制
下面,看看View的测量过程。
由源码可知,View的measure方法是final方法,子类不能修改它。最后会调用onMeasure方法。
public final void measure(int widthMeasureSpec, int heightMeasureSpec)
下面是onMeasure方法实现
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
上面的onMeasure方法调用setMeasuredDimension方法来设置View的宽高,下面来看看getDefaultSize方法的实现。
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
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;
}
可以知道,不同的测量模式有不同的测量大小。
在UNSPECIFIED模式下,传入的大小size就是返回的测量大小,其它模式都是measureSpec的specSize的大小。
下面看看获取size的方法
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
//mBackground是Drawable对象
private Drawable mBackground;
可以知道,如果View没有设置背景,那么测量的宽度就是mMinWidth ,对应属性android:minWidth的大小。如果设置了背静,就是Drawable的大小(Drawable没有设置大小默认是0)
下面,看看View的layout过程。
layout方法中会调用onLayout方法来确定View的位置,这时候的View的大小是实际展现出来的大小。
下面,看看View的绘制过程。
绘制过程
- 绘制背景backgroud.draw(canavs)
- 绘制自己(onDraw)
- 绘制children(dispacthDraw)
- 绘制装饰(onDrawScrollBars)