获取Canvas对象的方法
方法一:重写onDraw()、dispatchDraw()函数
- 构建方法:
protected void onDraw(canvas canvas){
super.onDraw();
}
protexted void dispatchDraw(Canvas canvas){
super.dispatchDraw(canvas);
}
-
可见上述两种方法中都有Canvas对象,这个对象是View中的Canvas对象,利用这个Canvas对象进行画图,效果会反映在View中
-
区别:
- onDraw()函数用于绘制视图自身
- dispatchDraw()函数用于绘制子视图
-
注意:
- 无论是View还是ViewGroup, 对这两个函数的调用顺序都是onDraw()->dispatchDraw(), 但是在ViewGroup中,当他有背景的时候就会调用onDraw()函数,没有背景的时候就会跳过onDraw()函数,直接调用dispatchDraw()函数,所以在ViewGroup中绘图直接使用dispatchDraw()
- 在View中, onDraw()和dispatchDraw()都会被调用,无论是将绘图代码放到onDraw()还是dispatchDraw()中,都将得到执行,但是由于dispatchDraw()用于绘制子视图,所以从这层定义上讲,我们使用onDraw()函数
-
总结:
- 绘制View控件,使用onDraw()函数
- 绘制VIewGroup控件, 使用dispatchDraw()函数
方法二:使用Bitmap创建
1.构造方法:
Canvas c = new Canvas(bitmap);
或者
Canvas c = new Canvas();
c.setBitmap(bitmap);
其中Bitmap可以从图片中加载,也可以自行创建
//从图片中加载
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.XXX, null);
//自行创建
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
2.在onDraw()函数中使用:
- 使用Bitmap构造了一个Canvas, 那么在这个Canvas上绘制的图像也会保存到这个Bitmap上,而不会画在View上。如果想要画在View, 就必须使用onDraw(Canvas canvas)函数中传入的Canvas来画一遍Bitmap。
eg:
public class BitmapCanvasView extends View {
private Bitmap bitmap;
private Paint paint;
private Canvas bitmapCanvas;
public BitmapCanvasView(Context context,AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setColor(Color.BLACK);
bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setTextSize(50);
//将文字卸载了bitmap上并没有将图片画在画布上,所以此时什么都不会有
bitmapCanvas.drawText("Wjx", 0, 100, paint);
//将bitmap显示出来
canvas.drawBitmap(bitmap,0, 0, paint);
}
}
方法三:调用SurfaceHolder.lockCanvas()函数
- 在使用SurfaceView的时候,当调用SurfaceHolder.lockCanvas()函数是,也会创建Canvas对象。
图层与画布:
- 之前提到过Canvas的save()和restore()函数,除这两个函数以外,还有一些其他的函数用来保存和恢复画布状态。
1.saveLayer()函数:
//保存指定矩形区域的Canvas内容:
//rect要保存的对应的矩形对象
//int saveFlags:取值有ALL_SAVE_FLAG\ MATRIX_SAVE_FLAG、CLIP_SAVE_FLAG、HAS_ALPHA_LAYER_SAVE_FLAG、FULL_COLOR_LAYER_SAVE_FLAG和CLIP_TO_LAYER_SAVE_FLAG,其中ALL_SAVE_FLAG表示保存全部内容
public int saveLayer(RectF rect, Paint paint, int saveFlags)
//使用四个点来构造矩形
public int saveLayer(float left, float top, float right, float bottom, Paint paint, int saveFlags)
eg:
public class XferModeView extends View {
private int width = 400;
private int height = 400;
private Bitmap dstBitmap;
private Bitmap srcBitmap;
private Paint paint;
public XferModeView(Context context,AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_SOFTWARE, null);
paint = new Paint();
paint.setColor(Color.BLACK);
dstBitmap = makeDst(width, height);
srcBitmap = makeSrc(width, height);
}
/**
* 创建一张矩形图片
* @param width
* @param height
* @return
*/
static Bitmap makeSrc(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(0xFF66AAFF);
c.drawRect(0, 0, width, height,paint);
return bitmap;
}
/**
* 创建一张圆形图片
* @param width
* @param height
* @return
*/
static Bitmap makeDst(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0 ,width, height), p);
return bitmap;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
int layerId = canvas.saveLayer(0, 0, width * 2, height * 2, paint, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(dstBitmap, 0, 0 ,paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBitmap, width/2, height/2, paint);
paint.setXfermode(null);
canvas.restoreToCount(layerId);
}
}
-
当注释掉layerId和restoreToCount时会发现结果变得不同
-
分析调用saveLayer()函数时的绘图流程:
在调用saveLayer()函数时,会生成一块全新的画布(Bitmap),这块画布的大小就是我们指定的所要保存的大小。新生成的画布是全透明的,在调用saveLayer()函数后所有的绘图操作都是在这块画布上进行的,调用drawXXX会生成一个透明的图层来专门绘制这个图形,而每次生成的图层都会叠加到最近的画布上,调用XFermode算法会将计算好的画布整体覆盖在原始的画布上。
- 分析没有调用saveLayer()函数时绘制的流程:
去掉saveLayer()之后就不会 新建一个画布,所有的绘图操作都会在最底层的画布上进行操作,所以调用Xfermode算法的时候计算出来的图像是 针对的底层不透明的绿色背景
- **结论:**调用saveLayer()函数会创建出一块全新的透明画布,大小与指定保存的区域大小一致,其后的绘图操作都放在这块画布上行,在绘制结束后,会覆盖在原=原始画布上进行展示。