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的绘制过程遵循如下几步:
- drawBackground(canvas);
- 绘制自己(onDraw);
- 绘制Children(dispatchDraw);
- 绘制装饰(onDrawScrollBars);
总结
从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都需要实现该方法,因其是个空方法;