android canvas layer (图层)详解与进阶
1 概述
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背景,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);