Android视图绘制流程解析(三)

紧接着上一篇的分析Android视图绘制流程解析(二),我们接下来看onDraw进行绘制。

onDraw()
measure和layout的过程都结束后,接下来就进入到draw的过程了,draw()方法内部的绘制过程总共可以分为六步,其中第二步和第五步在一般情况下很少用到,因此这里我们只分析简化后的绘制过程。

/*
         * 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)
         */
public void draw(Canvas canvas) {
	// 所有的视图最终都是调用 View 的 draw ()绘制视图( ViewGroup 没有复写此方法)
	// 在自定义View时,不应该复写该方法,而是复写 onDraw(Canvas) 方法进行绘制。
	// 如果自定义的视图确实要复写该方法,那么需要先调用 super.draw(canvas)完成系统的绘制,然后再进行自定义的绘制。
	int saveCount;
    if (!dirtyOpaque) {
        // step1:绘制本身View背景
        drawBackground(canvas);
    }

	final int viewFlags = mViewFlags;
    if (!verticalEdges && !horizontalEdges) {
    	if (!dirtyOpaque) 
            // step3:绘制本身View内容(默认为空实现),自定义View时需要进行复写
            onDraw(canvas);
        }
    }

	//step4:绘制子View(默认为空实现),单一View中不需要实现,ViewGroup中已经实现该方法
   	dispatchDraw(canvas);

	//step6:绘制滑动条和前景色等等
    onDrawScrollBars(canvas);

	return;
}

protected void onDraw(Canvas canvas) {
}

无论是ViewGroup还是单一的View,都需要实现这套流程,不同的是,在ViewGroup中,实现了 dispatchDraw()方法,而在单一子View中不需要实现该方法。自定义View一般要重写onDraw()方法,在其中绘制不同的样式。

接下来我们看一个示例:

public class MyView extends View {
	private Paint mPaint;
 
	public MyView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	}
 
	@Override
	protected void onDraw(Canvas canvas) {
		mPaint.setColor(Color.YELLOW);
		canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
		mPaint.setColor(Color.BLUE);
		mPaint.setTextSize(20);
		String text = "Hello View";
		canvas.drawText(text, 0, getHeight() / 2, mPaint);
	}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <com.example.viewtest.MyView 
        android:layout_width="200dp"
        android:layout_height="100dp"
        />
</LinearLayout>

View的绘制过程遵循如下几步:

  1. drawBackground(canvas);
  2. 绘制自己(onDraw);
  3. 绘制Children(dispatchDraw);
  4. 绘制装饰(onDrawScrollBars);
    Android视图绘制流程解析(三)

总结
从View的测量、布局和绘制原理来看,要实现自定义View,根据自定义View的种类不同,可能分别要自定义实现不同的方法。但是这些方法不外乎:onMeasure()方法,onLayout()方法,onDraw()方法。

  • onMeasure()
    单一View,一般重写此方法,针对wrap_content情况,规定View默认的大小值,避免于match_parent情况一致;
    ViewGroup,若不重写,就会执行和单子View中相同逻辑,不会测量子View,一般会重写onMeasure()方法,循环测量子View;
  • onLayout()
    单一View,不需要实现该方法。ViewGroup必须实现,该方法是个抽象方法,实现该方法,来对子View进行布局;
  • onDraw()
    无论单一View,或者ViewGroup都需要实现该方法,因其是个空方法;
    Android视图绘制流程解析(三)