一.View绘制流程
DecorView是View的顶级View,它需要通过ViewRootImpl建立DecorView和WindowManager的连接,通过ViewRootImpl的performTraversals开始向下测量,详细源码流程,请看
Android View源码解读:浅谈DecorView与ViewRootImpl
接着,底层View或者ViewGroup调用onMeasure,onLayout,onDraw去对View进行测量,布局,绘制,最终完成View的初始化,流程图如下
二.View的绘制流程
1.measure过程
View的measure是通过父容器和其本身的LayoutParams算出的MeasureSpec,最终测量出View的宽高,了解MeasureSpec请看 MeasureSpec详解
默认View的onMeasure的源码是:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
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;
}
只要MeasureSpec的模式是AT_MOST或者EXACTLY,最终size都等于MeasureSpec.getSize(measureSpec),所以当View的layoutParams设置为Wrap_Content的时候,默认size会等于父容器的size,所以在自定义View的时候,要重写wrap_content的情况,一般格式如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec)
);
}
private int measureWidth(int widthMeasureSpec) {
int result = 0;
int spaceMode = MeasureSpec.getMode(widthMeasureSpec);
int spaceSize = MeasureSpec.getSize(widthMeasureSpec);
if (spaceMode == MeasureSpec.EXACTLY){
result = spaceSize;
}else{
result = 200;//设置宽度默认值
if (spaceMode == MeasureSpec.AT_MOST){
result = Math.min(result,spaceSize);
}
}
return result;
}
这样就完成了View的测量!!
2.layout布局
在View或者ViewGroup中,有
protected void onLayout( int l, int t, int r, int b) {
方法,在其里面调用
public void layout(int l, int t, int r, int b)
完成View在父容器的位置布局
3.Draw绘制
在View中调用view方法开始绘制
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* 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, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
}
主要步骤有:
drawBackground(canvas):绘制背景
onDraw(canvas):绘制自身
dispatchDraw(canvas):绘制子View
onDrawForeground(canvas):绘制滚动条等
View的更新主要有以下方法:
invalidate:在主线程调用
postInvalidate:是在非主线程调用
requestLayout():方法会调用measure过程和layout过程,不会调用draw过程,也不会重新绘制任何View包括该调用者本身。