随笔博文

android canvas layer (图层)详解与进阶

2022-12-13 13:01:50 michael007js 2117

1 概述

在使用相关方法和flag的时候,先关闭硬件加速。如果需要开启,参照谷歌官方的硬件加速表格。硬件加速版本

2 saveLayer

saveLayer可以为canvas创建一个新的透明图层,在新的图层上绘制,并不会直接绘制到屏幕上,而会在restore之后,绘制到上一个图层或者屏幕上(如果没有上一个图层)。为什么会需要一个新的图层,例如在处理xfermode的时候,原canvas上的图(包括背景)会影响src和dst的合成,这个时候,使用一个新的透明图层是一个很好的选择。又例如需要当前绘制的图形都带有一定的透明度,那么创建一个带有透明度的图层,也是一个方便的选择。

public int saveLayer(RectF bounds, Paint paint, int saveFlags)  
public int saveLayer(float left, float top, float right, float bottom,Paint paint, int saveFlags)

上下两个方法都差不多,只是第一个方法接收的是RectF,第二个是坐标。都是指定Layer的大小和范围的。 saveFlags:代表了需要保存哪方面的内容,这里一共有6种取值,分别是

MATRIX_SAVE_FLAG,
CLIP_SAVE_FLAG,
HAS_ALPHA_LAYER_SAVE_FLAG,
FULL_COLOR_LAYER_SAVE_FLAG,
CLIP_TO_LAYER_SAVE_,
ALL_SAVE_FLAG。//保存全部

saveLayer的作用我们在之前的canvas变换中其实已经讲解过一部分,这里我们再看一下:

saveLayer saveLayer

上图中,绿色是canvas背景,dst是一个黄色圆形,src为一个蓝色正方形,xfermode为xor,可以看到,左边图形合成并不正常,原因就在于没有新开一个图层,而是直接在canvas上合成,受了背景颜色的影响,背景和dst这个黄色圆形一并被当作了dst,所以xor就成了这个图像,中间的正方形被除去了。 而右边的图中,新开了一个透明图层,因此,dst和src和合成没有受到其他干扰。合并之后正常显示。

代码如下:

canvas.drawColor(Color.GREEN);

//      canvas.saveLayer(0, 0, 1000, 1000, paint, Canvas.ALL_SAVE_FLAG);
canvas.translate(x, y);
canvas.drawBitmap(mDstB, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
canvas.drawBitmap(mSrcB, 0, 0, paint);
paint.setXfermode(null);
canvas.restore();

其中左图没有打开注释代码,右图打开了注释代码。

从上面可以看出来,canvas调用saveLayer之后,开启了一个新的透明图层。绘制完成后再合并到上一个图层上。

3 saveLayerAlpha

该方法可以开启一个带有透明度的图层,上面绘制的图像都会带有透明度,这样在需要绘制有透明度的图形时比较方便。

public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)  
public int saveLayerAlpha(float left, float top, float right, float bottom,int alpha, int saveFlags)

比上面的saveLayer方法多了一个alpha参数,也比较好理解。 alpha参数的取值是0到255 看一个示例图片:

这里写图片描述

可以看到,红色的圆圈有透明度,这里开启了一个带有一定透明度的图层。

paint.setColor(Color.BLUE);
canvas.drawCircle(100, 100, 100, paint);

paint.setColor(Color.RED);
canvas.saveLayerAlpha(100, 100, 300, 300, 120, Canvas.ALL_SAVE_FLAG);
canvas.drawCircle(200, 200, 100, paint);
canvas.restore();

4 saveFlags

上面我们只是粗暴的使用了ALL_SAVE_FLAG来保存的所有的信息,但是实际使用中,所有信息都保存必然增加了开销,所以,我们应该根据需要的动作,尽量的精确的保存少量的信息。这里就需要了解各个flag的意义。

首先需要知道的是,使用flag的方法除了saveFlayer还有save方法,他们都可以使用flag来指定需要保存的信息。那么来看看6中flag所对应的意义:

Flag意义适用方法
MATRIX_SAVE_FLAG只保存图层的matrix矩阵save,saveLayer
CLIP_SAVE_FLAG只保存大小信息save,saveLayer
HAS_ALPHA_LAYER_SAVE_FLAG表明该图层有透明度,和下面的标识冲突,都设置时以下面的标志为准saveLayer
FULL_COLOR_LAYER_SAVE_FLAG完全保留该图层颜色(和上一图层合并时,清空上一图层的重叠区域,保留该图层的颜色)saveLayer
CLIP_TO_LAYER_SAVE_创建图层时,会把canvas(所有图层)裁剪到参数指定的范围,如果省略这个flag将导致图层开销巨大(实际上图层没有裁剪,与原图层一样大)
ALL_SAVE_FLAG保存所有信息save,saveLayer

(1) MATRIX_SAVE_FLAG

只保存图层的matrix矩阵。 canvas中的哪些方法是利用matrix完成的,这里需要明确,其实我们知道,canvas的绘制,最终是发生在bitmap上的,从canvas的构造函数中也可以看出。在Bitmap的构造函数中可以看出:

Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)

bitmap的操作也是通过matrix来进行的。 那么我们可以知道canvas的canvas.translate(平移)、canvas.rotate(旋转)、canvas.scale(缩放)、canvas.skew(扭曲)其实都是通过matrix来达到的,这一点可以在代码中使用MATRIX_SAVE_FLAG来进行验证。

save方法

这里举例平移:

这里写图片描述

代码如下:

paint.setColor(Color.BLUE);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(200, 200);
canvas.drawRect(100, 100, 300, 300, paint);
canvas.restore();

paint.setColor(Color.RED);
canvas.drawRect(100, 100, 300, 300, paint);

可以看到平移效果得到了保存,并且可以恢复。

saveLayer方法

上面看了save方法,这里看看saveLayer是否有相同效果:

这里写图片描述

图中看出效果相同,代码如下

paint.setColor(Color.BLUE);
int count=canvas.saveLayer(0,0,1000,1000,paint,Canvas.MATRIX_SAVE_FLAG|Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
canvas.translate(200, 200);
canvas.drawRect(100, 100, 300, 300, paint);
canvas.restoreToCount(count);

paint.setColor(Color.RED);
canvas.drawRect(100, 100, 300, 300, paint);

代码基本相同,只是save更换为了saveLayer。

如果这里不使用MATRIX_SAVE_FLAG标志位,那么是否会出现不同的效果呢,使用CLIP_SAVE_FLAG标志来试试:

这里写图片描述

代码如下:

paint.setColor(Color.BLUE);
int count=canvas.saveLayer(0,0,1000,1000,paint,Canvas.CLIP_SAVE_FLAG|Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
canvas.translate(200, 200);
canvas.drawRect(100, 100, 300, 300, paint);
canvas.restoreToCount(count);

paint.setColor(Color.RED);
canvas.drawRect(100, 100, 300, 300, paint);

代码和上面基本相同,只是标志位改变了,这里可以看到两个图重叠了,也就是说CLIP_SAVE_FLAG标志位并没有保存相关的位移信息,导致restore的时候没能恢复。

(2) CLIP_SAVE_FLAG

看了上面的MATRIX_SAVE_FLAG,这里的意义基本知道,主要就是保存裁剪相关的信息。 由于和上面的示例基本类似,这里就不再做讲解了。

(3) FULL_COLOR_LAYER_SAVE_FLAG 和 HAS_ALPHA_LAYER_SAVE_FLAG

这两个方法是saveLayer专用的方法,HAS_ALPHA_LAYER_SAVE_FLAG为layer添加一个透明通道,这样一来没有绘制的地方就是透明的,覆盖到上一个layer的时候,就会显示出上一层的图像。而FULL_COLOR_LAYER_SAVE_FLAG 则会完全展示当前layer的图像,清除掉上一层的重合图像。

来看看FULL_COLOR_LAYER_SAVE_FLAG 的示例:

这里写图片描述

代码如下:

canvas.drawColor(Color.RED);

canvas.saveLayer(200,200,700,700,mPaint,Canvas.FULL_COLOR_LAYER_SAVE_FLAG);
mPaint.setColor(Color.GREEN);
canvas.drawRect(300,300,600,600,mPaint);
canvas.restore();

可以看到,绿色的方块周围有白色的一圈,整个白色加上绿色区域是这个layer的区域,由于这里使用了FULL_COLOR_LAYER_SAVE_FLAG标志,所以这块区域的红色被layer层完全覆盖(即使是透明),由于绿色周围的颜色是透明的,所以在清除了红色并覆盖后,就显示出了activity的背景颜色,所以显示了白色。

如果activity背景是黑色,这一块自然变为黑色:

这里写图片描述

那么其他代码不变,只是将标志位替换成HAS_ALPHA_LAYER_SAVE_FLAG会发生什么:

这里写图片描述

可以看到,绿色周围的白色不见了,可见,这就是区别。使用这个标志位不会清空上一图层的内容。

(4) CLIP_TO_LAYER_SAVE_

这个标志比较重要,官方的建议是,最好不要忽略这个标识,这个标识如果不设置将会带来很大的性能问题。

这个标识的作用是将canvas裁剪到指定的大小,并且无法回复。看下面一个例子:

这里写图片描述

再看下代码:

canvas.drawColor(Color.RED);
canvas.saveLayer(200,200,700,700,mPaint,Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.drawColor(Color.GREEN);
canvas.restore();
canvas.drawColor(Color.BLACK);

这里看,先将底色绘制为红色,然后开启新图层,再绘制为绿色,最后将canvas绘制为黑色,为什么最后不是全屏黑色呢,这里明明restore了,这是因为使用了CLIP_TO_LAYER_SAVE_FLAG标志,这样一来,canvas被裁剪了,并且无法回复了。这样也就减少了处理的区域,增加了性能。


首页
关于博主
我的博客
搜索