View的绘制从ViewRootImpl中的performTraversals()中开始进行,其主要绘制过程分为三步:
1、measure()测量;
2、layout()布局;
3、draw()画图;
其调用顺序为:measure->layout->draw
其中measure()内部将调用onMeasure(),layout()内部将调用onLayout(),draw()内部将调用onDraw(),View的子类通过重写onMeasure()、onLayout()、onDraw()方法,用以显示出不同的控件展现形式。
measure():测量视图大小。
其中MeasureSpec是View的绘制中较为关键的类。MeasureSpec为一个int型整数4字节,即32位。它由参数大小(specSize)和参数的规格(specMode)共同决定。一般使用方法时,传入父View的specSize和子View的specMode共同生成一个对应的spec。
对于specMode主要有三种类型,EXACTLY、AT_MOST、UNSPECIFIED。其占用了MeasureSpec中的前2位,后30位存放specSize的大小。ViewRoot中通过getRootMeasureSpec()获取子View的大小,判断逻辑如下:
private int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
简单理解为:
传入屏幕的宽高数据和DecorView的specMode,
通过判断rootDimension的类型,确定DecorView的spec值。
当为MATCH_PARENT时:测量规格为一个精确值EXACTLY,其大小为屏幕大小。
当为WRAP_CONTENT时:测量规格为设置最大值,大小最多为屏幕大小。
当为Default时,测量规格设置为一个精确值,大小为其自己指定的大小。
设置完DecorView后,依次遍历其子View对其使用类似方法逐次调用其ondraw()方法测量,由于不同View的onDraw()测量规则不一样导致其测量的最终结果不一样,因此自定义View需要重写其ondraw方法进行规则设定。
layout():布局视图位置。发生在measure之后。对于不同的ViewGroup实现类(如LinearLayout、RelativeLayout)其onLayout的实现有所不同。
布局过程完毕后,将mLeft、mTop、mRight、mBottom进行赋值,确定出View自身的位置,子View的位置由不同View重写的onLayout()规则进行布局。
draw():根据layout绘制view。主要分为6步:
1、绘制View的背景
2、保存当前图层信息(可选)
3、绘制View的内容
4、绘制子View
5、绘制View边缘效果(可选)
6、绘制View的相关装饰(滚动条等)
其绘制子View的过程主要将canvas画布传入到子View实现的ondraw方法中,然后通过不同view的draw规则进行绘制。
1、measure()测量;
2、layout()布局;
3、draw()画图;
其调用顺序为:measure->layout->draw
其中measure()内部将调用onMeasure(),layout()内部将调用onLayout(),draw()内部将调用onDraw(),View的子类通过重写onMeasure()、onLayout()、onDraw()方法,用以显示出不同的控件展现形式。
measure():测量视图大小。
其中MeasureSpec是View的绘制中较为关键的类。MeasureSpec为一个int型整数4字节,即32位。它由参数大小(specSize)和参数的规格(specMode)共同决定。一般使用方法时,传入父View的specSize和子View的specMode共同生成一个对应的spec。
对于specMode主要有三种类型,EXACTLY、AT_MOST、UNSPECIFIED。其占用了MeasureSpec中的前2位,后30位存放specSize的大小。ViewRoot中通过getRootMeasureSpec()获取子View的大小,判断逻辑如下:
private int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
简单理解为:
传入屏幕的宽高数据和DecorView的specMode,
通过判断rootDimension的类型,确定DecorView的spec值。
当为MATCH_PARENT时:测量规格为一个精确值EXACTLY,其大小为屏幕大小。
当为WRAP_CONTENT时:测量规格为设置最大值,大小最多为屏幕大小。
当为Default时,测量规格设置为一个精确值,大小为其自己指定的大小。
设置完DecorView后,依次遍历其子View对其使用类似方法逐次调用其ondraw()方法测量,由于不同View的onDraw()测量规则不一样导致其测量的最终结果不一样,因此自定义View需要重写其ondraw方法进行规则设定。
layout():布局视图位置。发生在measure之后。对于不同的ViewGroup实现类(如LinearLayout、RelativeLayout)其onLayout的实现有所不同。
布局过程完毕后,将mLeft、mTop、mRight、mBottom进行赋值,确定出View自身的位置,子View的位置由不同View重写的onLayout()规则进行布局。
draw():根据layout绘制view。主要分为6步:
1、绘制View的背景
2、保存当前图层信息(可选)
3、绘制View的内容
4、绘制子View
5、绘制View边缘效果(可选)
6、绘制View的相关装饰(滚动条等)
其绘制子View的过程主要将canvas画布传入到子View实现的ondraw方法中,然后通过不同view的draw规则进行绘制。