android View绘制源码分析
在开发过程中我们经常要进行view的自定义。如果熟练掌握自定义技巧的话就能做出很多控件出来。这篇博客来讲讲view绘制背后发生的那些事。
一, view的基础知识
view的绘制概括
首先先说说view绘制的整体过程。
View绘制的源码分析 ,它的三大流程都是在ViewRootImpl中完成的,从ViewRootImpl中的performTraversals开始,有三个方法performMeasure,performLayout,prformDraw分别对measure,layout,draw三个方法。在onMeasure对所有子元素进行measure过程 ,这时measure就从父容器传递到子元素。子元素重复父元素的过程。layout与draw类似,只是draw通过diapatchDraw来实现 。
measure完成后可以通过getMeasureWidth,getMeasureHeight分别获取View测量后的宽高。在实际情况下几乎所有情况它都等于最终宽高。layout过程决定view的四个顶点的坐标和实际view的宽高,完成之后可以通过getTop,getBottom,getLeft,getRight来拿 到view的四个顶点位置。并通过getWidth()和getHeight()来拿到最终宽高。draw决定了view的显示,只有完成才能显示在屏幕上。
MeasureSpec
在测量过程中系统会将View的LayoutParams根据容器所施加的规则转换成对应的MeasureSpec,然后再根据这个测量出view。
Measure是一个32位的int,高2位代表SpecMode,低30位代表SpecSize。SpecMode表示测量模式,SpecSize指在某种测量模式下规格的大小。其代码如下:
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY = 1 << MODE_SHIFT;
public static final int AT_MOST = 2 << MODE_SHIFT;
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK) ;
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK) ;
}
其实MeasureSpec中源码很值得我们学习。他用一个32位的int来表示模式和大小,节省了空间,也更直观。MeasureSpec通过将specMode和specSize打包成一个int来避免过多的对象内存分配。以上是MeasureSpec的打包和解包过程。
specMode有三种状态:UNSPECIFIED,EXACTLY(相当于match_parent和精确值这两种模式),AT_MOST(wrap_content)。
LayoutParams
对于一般容器,它的MeasureSpec是由父容器的MeasureSpec和自身的LayoutParams共同决定的。上篇博客LayoutInflater源码解析 我们己经介绍了android view的结构,PhoneWindow包了一层DecorView,DecorView里才是title和我们的content view。所以行分析DecorView。
先来看下DecorView的产生源码:
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
再看下getRootMeasureSpec方法:
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;
}
这里很清楚,分别分MatchPraent和wrap_content和自定义来计算宽高。再来看下普通的view,在ViewGroup的measureChildWIthMargins中:
protected void measureChildWithMargins (View child,
int parentWidthMeasureSpec , int widthUsed,
int parentHeightMeasureSpec , int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams() ;
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) ;
child.measure(childWidthMeasureSpec , childHeightMeasureSpec);
}
再看下getChildMeasureSpec:
public static int getChildMeasureSpec(int spec, int padding , int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec. getSize(spec) ;
int size = Math. max( 0, specSize - padding) ;
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec. EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size ;
resultMode = MeasureSpec. EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size ;
resultMode = MeasureSpec. AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension ;
resultMode = MeasureSpec. EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size ;
resultMode = MeasureSpec. AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size ;
resultMode = MeasureSpec. AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension ;
resultMode = MeasureSpec. EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = 0;
resultMode = MeasureSpec. UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = 0;
resultMode = MeasureSpec. UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode) ;
}
以上表明,如果父是EXACTLY,parentSize,那么子如果是EXACTLY,
1)具体的值size:那子的MeasureSpec就是EXACTLY,size;
2)MATCH_PARENT:那子的MeasureSpec就是EXACTLY,parentSize;
3)WRAP_CONTENT:那子的MeasureSpec就是AT_MOST,parentSize;
如果父是ATMOST,parentSize,那么子如果是EXACTLY,
1)具体的值size:那子的MeasureSpec就是EXACTLY,size;
2)MATCH_PARENT:那子的MeasureSpec就是A