09.1 精通自定义 View 之 Canvas 与图层——获取 Canvas 对象的方法

返回自定义 View 目录

9.1.1 方法一:重写 onDraw()、dispatchDraw() 函数

一般在自定义 View 时,我们都会重写 onDraw()、dispatchDraw() 函数。先来看一下 onDraw()、dispatchDraw() 函数的定义,如下:

1
2
3
4
5
6
7
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
}

可以看到,onDraw()、dispatchDraw() 函数在传入的参数中都有一个 Canvas 对象。这个 Canvas 对象是 View 中的 Canvas 对象,利用这个 Canvas 对象绘图,效果会直接反映在 View 中。

onDraw()、dispatchDraw() 函数的区别如下:

  • onDraw() 函数用于绘制视图自身。
  • dispatchDraw() 函数用于绘制子视图。

无论是 View 还是 ViewGroup 对这连个函数的调用顺序都是 onDraw() —> dispatchDraw()。

但在 ViewGroup 中,当它有背景的时候就会调用 onDraw() 函数,否则就会跳过 onDraw() 函数,直接调用 dispatchDraw() 函数。所以,如果要在 ViewGroup 中绘图,往往会重写 dispatchDraw() 函数。

在 View 中,onDraw() 和 dispatchDraw() 函数都会被调用的,所以我们无论把绘图代码放在 onDraw() 函数或者 dispatchDraw() 函数中都是可以得到效果的。但是,由于 dispatchDraw() 函数用于绘制子控件,所以,原则来上讲,在绘制 View 控件时,我们会重写 onDraw() 函数。

总结:在绘制 View 控件时,需要重写 onDraw() 函数;在绘制 ViewGroup 时,需要重写 dispatchDraw() 函数。

9.1.2 方法二:使用 Bitmap 创建

1. 构建方法

使用:

1
Canvas c = new Canvas(bitmap);


1
2
Canvas c = new Canvas();
c.setBitmap(bitmap);

其中,bitmap 可以从图片中加载,也可以自行创建。

1
2
3
4
// 方法一:新建一个空白 bitmap
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// 方法二:从图片中加载
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.img, null);

除这两种方法以外,还有其他几种方法(比如构造一个具有 Matrix 的图像副本),这里不再涉及,大家可以去查看 Bitmap 的构造函数。

2. 在 onDraw() 函数中使用

需要注意的是,如果我们用 Bitmap 构造了一个 Canvas,那这个 Canvas 上绘制的图像也都会保存在这个 Bitmap 上,而不是画在 View 上。如果想画在 View 上就必须使用 OnDraw(Canvas canvas) 函数中传入的 Canvas 画一遍 Bitmap。

下面举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TestView extends View {
private Paint mPaint;
private Bitmap mBitmap;
private Canvas mBitmapCanvas;
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setTextSize(50);
mPaint.setColor(Color.RED);
mBitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
mBitmapCanvas = new Canvas(mBitmap);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mBitmapCanvas.drawText("先小涛", 100, 100, mPaint);
// canvas.drawBitmap(mBitmap, 0, 0, mPaint);
}
}

运行上述代码后会发现,结果是一片空白,我们写的字去哪儿了?在 onDraw() 函数中,我们只是将文字画在了 mBitmapCanvas 上,也就是我们新建 mBitmap 图片上,而最终没有将图片画在画布上。因为文字被写在了图片上,而画布上却没有任何内容,所以结果是一片空白。如果将注释掉的最后一句打开,即可将图片画在画布上,在视图上就会显示文字了,如下图所示。

9.1.3 方法三:调动 SurfaceHolder.lockCanvas() 函数

在使用 SurfaceView 时,当调用 SurfaceHolder.lockCanvas() 函数时,也会创建 Canvas 对象,有关 SurfaceView 的知识可以参考第 10 章。