上一节,我们知道view绘制流程,下面我们针对每一个步骤分析:
graph LR
performMeasure --> performLayout
performLayout --> performDraw
==performMeasure== 测量view尺寸规格
performMeasure方法进去,实际上看到的是调用mView.measure,而mView实际上是我们的顶层decorView
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
mView.measure进去实际是一个final方法,是不可以被子类重写的,这个方法首先会从缓存里面找,找不到的情况下会继续调用==onMeasure==方法。
graph LR
onMeasure-->setMeasuredDimension
setMeasuredDimension-->setMeasuredDimensionRaw
上面这几个方法调用后的目的只有一个,对==mMeasuredWidth== 和 ==mMeasuredHeight==进行赋值操作
接下里要了解view的测量过程,那么我们首先需要了解一个类:==MeasureSpec==
view的测量,需要测量模式+尺寸两部分,而MeasureSpec是对view的测量规格的一个封装。
MeasureSpec里面对应一个32位的int值,例如:00000000000000000000000000000000
前2位数字代表的是模式,后面30位代表的是尺寸
SpecMode(前2位,模式) + SpecSize(后30,尺寸)
MeasureSpec的源码里面定义了三种模式:
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
0左移30位,二进制的值:00000000000000000000000000000000
父容器不对View做任何限制,系统内部使用
public static final int EXACTLY = 1 << MODE_SHIFT;
1左移30位,二进制的值:01000000000000000000000000000000
父容器检测出View的大小,Vew的大小就是SpecSize, LayoutPamras是 match_parent or 固定大小
public static final int AT_MOST = 2 << MODE_SHIFT;
2左移30位,二进制的值:10000000000000000000000000000000
父容器指定一个可用大小,View的大小不能超过这个值,LayoutPamras是 wrap_content
我们了解完MeasureSpec后,,看下performMeasure的参数,参数是由==getRootMeasureSpec==方法测量出来的,getRootMeasureSpec这个方法参数的含义是:==windowSize对应的是窗口的大小尺寸,rootDimension是自身(decorView)的LayoutParams属性==
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
上面可以看出,测量出decorview的尺寸后,传进了performMeasure方法,然后继续传入到了==mView.measure(childWidthMeasureSpec, childHeightMeasureSpec)== 方法,mView是decorView,是继承于FrameLayout的,所以实际上调用到了FrameLayout 的 measure方法。
通过看FrameLayout的源码可以知道,FrameLayout调用==measureChildWithMargins==方法测量出子view的尺寸规格,然后调用子 view的 measure 方法,这样子形成了一个递归测量效果,需要注意的是自定义view的时候若不重写onMeasure方法,不管自定义的view是match_parent,wrap_content,最后的大小尺寸都是父容器的尺寸大小
在测量出所有的子控件尺寸后,经过一系列的计算,调用==setMeasuredDimensionRaw== 方法保存自身的宽高尺寸信息
总结:
ViewGroup measure --> onMeasure(测量子控件的宽高)--> setMeasuredDimension -->setMeasuredDimensionRaw(保存自己的宽高)
View measure --> onMeasure --> setMeasuredDimension -->setMeasuredDimensionRaw(保存自己的宽高)
==performLayout== 决定view位置
performLayout(lp, mWidth, mHeight);
lp: 顶层布局的layoutParams
mWidth:顶层布局的宽
mHeight:顶层布局的高
在源码里面可以看到,host是顶层布局decorview,通过调用==layout==方法来确定自身的位置,即mLeft,mTop,mRight,mBottom,这四个位置是通过调用==setFrame==方法来实现赋值
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
如果是viewGroup,则通过调用==onLayout==方法确定子view的位置
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
总结:
ViewGroup layout(来确定自己的位置,4个点的位置) -->onLayout(进行子View的布局)
View layout(来确定自己的位置,4个点的位置)
==performDraw== 绘制view
graph LR
performDraw-->draw
draw-->drawSoftware
drawSoftware-->mView.draw
从上面可以看到,view的绘制时,调用到了mView.draw方法,此时的mView是DecorView。
从draw方法的源码注释里面可以大概看到绘制view的过程和步骤:
/*
* 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)
*/
总结:
ViewGroup
绘制背景 drawBackground(canvas)
绘制自己onDraw(canvas)
绘制子View dispatchDraw(canvas)
绘制前景,滚动条等装饰onDrawForeground(canvas)
View
绘制背景 drawBackground(canvas)
绘制自己onDraw(canvas)
绘制前景,滚动条等装饰onDrawForeground(canvas)
网友评论