整个View树的绘制流程是在ViewRoot.java类中的performTraversals()方法展开的,该函数的执行过程可简单概况为:根据之前设置的状态,判断是否需要重新计算视图大小(measure)、是否需要重新安置视图的位置(layout)、以及是否需要重新绘制(draw)。


流程一:mesarue()测量过程




This is called to find out how big a view should be. The parent supplies constraint information in the width and height parameters.
The actual mesurement work of a view is performed in onMeasure(int,int), called by this method. Therefore, only onMeasure(int,int) can and must be 

overriden by subclasses.

Parameters:
	widthMeasureSpec Horizontal space requirements as imposed by the parent
	heightMeasureSpec Vertical space requirements as imposed by the parent
See also:
	onMeasure(int,int)

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
	if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
		widthMeasureSpec != mOldWidthMeasureSpec ||
		heightMeasureSpec != mOldHeightMeasureSpec) {

		// first clears the measured dimension flag
		mPrivateFlags &= ~MEASURED_DIMENSION_SET;
	
		if (ViewDebug.TRACE_HIERARCHY) {
			ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
		}

		// measure ourselves, this should set the measured dimension flag back
		onMeasure(widthMeasureSpec, heightMeasureSpec);

		// flag not set, setMeasuredDimension() was not invoked, we raise
		// an exception to warn the developer
		if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
			throw new IllegalStateException("onMeasure() did not set the"
				+ " measured dimension by calling"
				+ " setMeasuredDimension()");
		}

		mPrivateFlags |= LAYOUT_REQUIRED;
	}

	mOldWidthMeasureSpec = widthMeasureSpec;
	mOldHeightMeasureSpec = heightMeasureSpec;
}






// ViewRoot.java
// measure()过程  
// 发起measure()过程的"发号者"是在ViewRoot.java里的performTraversals()方法中的mView.measure()
private void performTraversals() {
	
	//... 
	View mView;
	mView.measure(h,l);
	//....

}

// 回调View视图里的onMeasure过程
private void onMeasure(int height , int width) {
	//设置该view的实际高(mMeasuredHeight)和宽(mMeasuredWidth) 
	//1、该方法必须在onMeasure调用,否者报异常。
	setMeasuredDimension(h , l);

	//2、如果该View是ViewGroup类型,则对它的每个子View进行measure()过程
	int childCount = getChildCount();

	for(int i = 0; i < childCount; i++){
		//2.1、获得每个子View对象引用
		View child = getChildAt(i);

		//整个measure()过程就是个递归过程
		//该方法只是一个过滤器,最后会调用measure()过程或者measureChild(child , h, i)方法
		measureChildWithMargins(child , h, i) ; 

		//其实,对于我们自己写的应用来说,最好的办法是去掉框架里的该方法,直接调用view.measure(),如下:
		//child.measure(h, l);
	}
}

// 该方法在ViewGroup.java里具体实现
protected void measureChildWithMargins(View v, int height , int width) {
	v.measure(h,l);
}




流程二:layout()布局过程






Assign a size and position to a view and all of its descendants
This is the second phase of the layout mechanism. (The first is measuring). In this phase, each parent calls layout on all of its children to position them. 

This is typically done using the child measurements that were stored in the measure pass().
Derived classes should not override this method. Derived classes with children should override onLayout. In that method, they should call layout on each 

of their children.

Parameters:
	l Left position, relative to parent
	t Top position, relative to parent
	r Right position, relative to parent
	b Bottom position, relative to parent

@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
	int oldL = mLeft;
	int oldT = mTop;
	int oldB = mBottom;
	int oldR = mRight;
	boolean changed = setFrame(l, t, r, b);	//设置每个视图位于父视图的位置坐标
	if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
		if (ViewDebug.TRACE_HIERARCHY) {
			ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
		}

		onLayout(changed, l, t, r, b);	//回调onLayout()方法,设置每个子视图的布局  
		mPrivateFlags &= ~LAYOUT_REQUIRED;

		ListenerInfo li = mListenerInfo;
		if (li != null && li.mOnLayoutChangeListeners != null) {
			ArrayList<OnLayoutChangeListener> listenersCopy =
				(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
			int numListeners = listenersCopy.size();
			for (int i = 0; i < numListeners; ++i) {
				listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
			}
		}
	}
	mPrivateFlags &= ~FORCE_LAYOUT;
}






// ViewRoot.java
// layout()过程
// 发起layout()过程的“发号者”是在ViewRoot.java里的performTraversals()方法中的mView.layout()  
private void  performTraversals(){

	//...
	View mView;
	mView.layout(left, top, right, bottom);
	//....
}

// 回调View视图里的onLayout过程,该方法只由ViewGroup类型实现
private void onLayout(int left, int top, right, bottom) {

	// 如果该View不是ViewGroup类型
	// 调用setFrame()方法设置该控件的在父视图上的坐标轴
	setFrame(l, t, r, b) ;

	//--------------------------

	// 如果该View是ViewGroup类型,则对它的每个子View进行layout()过程
	int childCount = getChildCount();
	for(int i = 0; i < childCount; i++){
		// 获得每个子View对象引用
		View child = getChildAt(i);
		// 整个layout()过程就是个递归过程
		child.layout(l, t, r, b);
	}
}




流程三:draw()绘制过程



        5、绘制滚动条。





/**
* Manually render this view (and all of its children) to the given Canvas.
* The view must have already done a full layout before this function is
* called.  When implementing a view, implement
* {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
* If you do need to override this method, call the superclass version.
*
* @param canvas The Canvas to which the View is rendered.
*/

public void draw(Canvas canvas) {

	if (ViewDebug.TRACE_HIERARCHY) {
		ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
	}


	final int privateFlags = mPrivateFlags;
	final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
		(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
	mPrivateFlags = (privateFlags & ~DIRTY_MASK) | 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) {
		final Drawable background = mBGDrawable;
		if (background != null) {
			final int scrollX = mScrollX;
			final int scrollY = mScrollY;
			
			if (mBackgroundSizeChanged) {
				background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
				mBackgroundSizeChanged = false;
			}

			if ((scrollX | scrollY) == 0) {
				background.draw(canvas);
			} else {
				canvas.translate(scrollX, scrollY);
				background.draw(canvas);
				canvas.translate(-scrollX, -scrollY);
			}
		}
	}

	// 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);

		// Step 6, draw decorations (scrollbars)
		onDrawScrollBars(canvas);

		// we're done...
		return;
	}

	/*
	* Here we do the full fledged routine...
	* (this is an uncommon case where speed matters less,
	* this is why we repeat some of the tests that have been
	* done above)
	*/
	
	boolean drawTop = false;
	boolean drawBottom = false;
	boolean drawLeft = false;
	boolean drawRight = false;
	
	float topFadeStrength = 0.0f;
	float bottomFadeStrength = 0.0f;
	float leftFadeStrength = 0.0f;
	float rightFadeStrength = 0.0f;

	// Step 2, save the canvas' layers
	int paddingLeft = mPaddingLeft;
	final boolean offsetRequired = isPaddingOffsetRequired();
	if (offsetRequired) {
		paddingLeft += getLeftPaddingOffset();
	}
	
	int left = mScrollX + paddingLeft;
	int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
	int top = mScrollY + getFadeTop(offsetRequired);
	int bottom = top + getFadeHeight(offsetRequired);

	if (offsetRequired) {
		right += getRightPaddingOffset();
		bottom += getBottomPaddingOffset();
	}
	
	final ScrollabilityCache scrollabilityCache = mScrollCache;
	final float fadeHeight = scrollabilityCache.fadingEdgeLength;
	int length = (int) fadeHeight;

	// clip the fade length if top and bottom fades overlap
	// overlapping fades produce odd-looking artifacts
	if (verticalEdges && (top + length > bottom - length)) {
		length = (bottom - top) / 2;
	}

	// also clip horizontal fades if necessary
	if (horizontalEdges && (left + length > right - length)) {
		length = (right - left) / 2;
	}

	if (verticalEdges) {
		topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
		drawTop = topFadeStrength * fadeHeight > 1.0f;
		bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
		drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
	}

	if (horizontalEdges) {
		leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
		drawLeft = leftFadeStrength * fadeHeight > 1.0f;
		rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
		drawRight = rightFadeStrength * fadeHeight > 1.0f;
	}

	saveCount = canvas.getSaveCount();

	int solidColor = getSolidColor();
	if (solidColor == 0) {
		final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

		if (drawTop) {
			canvas.saveLayer(left, top, right, top + length, null, flags);
		}

		if (drawBottom) {
			canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
		}

		if (drawLeft) {
			canvas.saveLayer(left, top, left + length, bottom, null, flags);
		}

		if (drawRight) {
			canvas.saveLayer(right - length, top, right, bottom, null, flags);
		}
	} else {
		scrollabilityCache.setFadeColor(solidColor);
	}
	
	// Step 3, draw the content
	if (!dirtyOpaque) onDraw(canvas);

	// Step 4, draw the children
	dispatchDraw(canvas);

	// Step 5, draw the fade effect and restore layers
	final Paint p = scrollabilityCache.paint;
	final Matrix matrix = scrollabilityCache.matrix;
	final Shader fade = scrollabilityCache.shader;

	if (drawTop) {
		matrix.setScale(1, fadeHeight * topFadeStrength);
		matrix.postTranslate(left, top);
		fade.setLocalMatrix(matrix);
		canvas.drawRect(left, top, right, top + length, p);
	}

	if (drawBottom) {
		matrix.setScale(1, fadeHeight * bottomFadeStrength);
		matrix.postRotate(180);
		matrix.postTranslate(left, bottom);
		fade.setLocalMatrix(matrix);
		canvas.drawRect(left, bottom - length, right, bottom, p);
	}

	if (drawLeft) {
		matrix.setScale(1, fadeHeight * leftFadeStrength);
		matrix.postRotate(-90);
		matrix.postTranslate(left, top);
		fade.setLocalMatrix(matrix);
		canvas.drawRect(left, top, left + length, bottom, p);
	}

	if (drawRight) {
		matrix.setScale(1, fadeHeight * rightFadeStrength);
		matrix.postRotate(90);
		matrix.postTranslate(right, top);
		fade.setLocalMatrix(matrix);
		canvas.drawRect(right - length, top, right, bottom, p);
	}

	canvas.restoreToCount(saveCount);

	// Step 6, draw decorations (scrollbars)
	onDrawScrollBars(canvas);
}






// ViewRoot.java
// draw()过程
// 发起draw()的“发号者”是在ViewRoot.java里的performTraversals()方法,该方法会继续调用draw()方法开始绘图  
private void  draw(){
	
	//...
	View mView;
	mView.draw(canvas);
	//....

}

// 回调View视图里的onDraw()过程,该方法只由ViewGroup类型实现
private void draw(Canvas canvas){
	// 该方法会做如下事情
	// 1、绘制该View的背景
	// 2、为绘制渐变框做一些准备操作
	// 3、调用onDraw()方法绘制视图本身
	// 4、调用dispatchDraw()方法绘制每个子视图,dispatchDraw()已经在Android框架中实现了,在ViewGroup方法中
	// 	应用程序程序一般不需要重写该方法,但可以捕获该方法的发生,做一些特别的事情
	//5、绘制渐变框    
}

// ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法  
@Override  
protected void dispatchDraw(Canvas canvas) {
	
	//
	// 其实现方法类似如下:
	int childCount = getChildCount();
	for(int i = 0; i < childCount; i++) {
		View child = getChildAt(i);
		//调用drawChild完成
		drawChild(child,canvas);
	}
}

// ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法  
protected void drawChild(View child,Canvas canvas) {

	// ....
	// 简单的回调View对象的draw()方法,递归就这么产生了
	child.draw(canvas);
	//.........  
}






引起View树重新绘制的因素有如下几种:

这三种情况,最终会直接或间接调用到三个方法,分别为invalidate(),requsetLaytout()以及requestFocus() ,接着这三个方法最终会调用到ViewRoot中的schedulTraversale()方法,该函数然后发起一个异步消息,消息处理中调用performTraverser()方法对整个View进行遍历。


invalidate()方法:


requestLayout()方法


requestFocus()方法


参考资料:

1、Android中View绘制流程以及invalidate()等相关方法分析