当一个应用启动的时候,会启动一个主activity,android系统会根据activity的布局来对它进行绘制。每个view负责绘制自己,而viewgroup还需要负责通知自己的子view进行绘制操作。视图绘制的过程可以分为三个步骤,分别是 Measure Layout 和 Draw
private void performTraversals() {
......
//最外层的根视图的widthMeasureSpec和heightMeasureSpec由来
//lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENT
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
......
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
......
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
......
mView.draw(canvas);
......
}
Measure
Measure操作用来计算View的实际大小,对于viewGroup来说,由viewGroup在它的measureChild方法中传递给子view,viewGroup通过遍历自身所有的子view,并逐个调用子view的measure方法完成测量工作。当测量某个指定的view的时候,根据父容器的MeasureSpec和子view的LayoutParams等信息来计算子view的MeasureSpec
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
....
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
// 遍历自己的子View,只要不是GONE的都会参与测量,
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
...
....
}
}
//测量某个指定的view
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
// 子View的LayoutParams,你在xml的layout_width和layout_height,
// layout_xxx的值最后都会封装到这个个LayoutParams。
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//根据父容器的MeasureSpec和子view的LayoutParams等信息来计算子view的MeasureSpec
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);
//通过父View的MeasureSpec和子View的自己LayoutParams的计算,算出子View的MeasureSpec,然后父容器传递给子容器的
// 然后让子View用这个MeasureSpec(一个测量要求,比如不能超过多大)去测量自己,如果子View是ViewGroup 那还会递归往下测量。
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
对于每个View的measure方法,最终的测量是通过回调onMeasure方法实现的。这个通常由view的特定子类自己实现,卡发着也可以通过重写这个方法实现自定义view。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
......
onMeasure(widthMeasureSpec,heightMeasureSpec);
.....
}
//如果需要自定义测量过程,则子类可以重写这个方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
//如果view没有重写onMeasure方法,则默认会调用getDefaultSize来获得view的宽高
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
//@param size参数一般表示设置了android:minHeight属性或者该View背景图片的大小值
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: //表示该View的大小父视图未定,设置为默认值
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
主要要三种测量模式,
UNSPECIFIED :不指定测量模式,俯视图没有限制子视图饿大小,子视图可以使想要的任何尺寸,通常用于系统内部,应用开发很少用到
EXACTLY 精确测量模式,当该视图的layout_width 或者layout_height 指定为具体数值或者match_parent时生效,表示父视图已经决定了子视图的精确大小,这种模式下View的测量值就是SpecSize的值
AT_MOST 最大值模式,当该视图的layout_width或者layout_height指定为wrap_content时生效,此时,子视图的尺寸可以使不超过父视图允许的最大尺寸的任何尺寸。
Layout
Layout 过程用来确定View在父容器中的布局位置,它是由父容器获取子View的位置参数后,调用子View的layout方法并将位置参数传入实现的,
Draw
Draw用来将控件绘制出来
public void draw(Canvas canvas) {
...
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, 如果需要的话,绘制view背景
...
background.draw(canvas);
...
// skip step 2 & 5 if possible (common case) 一般情况会跳过第二步和第五步
...
// Step 2, save the canvas layers 如果需要的话,保存canvas图层,为fading做准备
...
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
...
// Step 3, draw the content 绘制view的内容
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children 绘制view的子view
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers 如果需要的话,绘制View的fading边缘来恢复图层
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
canvas.drawRect(left, top, right, top + length, p);
}
...
// Step 6, draw decorations (scrollbars) 绘制View的装饰例如滚动条()
onDrawScrollBars(canvas);
}