Canvas与图层

2020-12-18  本文已影响0人  code希必地

1、Canvas

1.1、save()

为了实现一些效果不得不对画布进行操作,但是操作完了,画布的状态也改变了,这会严重影响后面的画图操作。如果能对画布的大小和状态(旋转角度、扭曲)进行实时保存和恢复就好了,这时就可以使用Canvas.save()对画布的状态进行保存,将其放入特定的栈中。注意:使用save()并不会创建新的画布,只是保存画布的状态而已。

override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawColor(Color.RED)
        //保存当前画布大小,即整屏
        canvas.save()
        canvas.clipRect(Rect(100, 100, 800, 800))
        canvas.drawColor(Color.GREEN)
        //恢复整屏画布
        canvas.restore()
        canvas.drawColor(Color.BLUE)
    }

在上面例子中,我们先将画布填充为红色,然后保存画布的状态。然后对画布进行裁剪并填充为绿色,然后还原画布,将其填充为蓝色。整个过程如下:


image.png

1.2、saveLayer()

saveLayer有两个构造函数

public int saveLayer(RectF bounds,Paint paint, int saveFlags) 
public int saveLayer(float left, float top, float right, float bottom, Paint paint, int saveFlags)
class XfermodeView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
    private val paint = Paint()
    private val bitmapWidth = 400
    private val bitmapHeight = 400
    private val srcBitmap = createSrcBitmap()
    private val dstBitmap = createDstBitmap()

    init {
        setLayerType(LAYER_TYPE_SOFTWARE, null)
        paint.style = Paint.Style.FILL
    }


    private fun createSrcBitmap(): Bitmap {
        val srcBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
        paint.setColor(Color.BLUE)
        val canvas = Canvas(srcBitmap)
        canvas.drawRect(Rect(0, 0, bitmapWidth, bitmapHeight), paint)
        return srcBitmap
    }

    private fun createDstBitmap(): Bitmap {
        val dstBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
        paint.setColor(Color.YELLOW)
        val canvas = Canvas(dstBitmap)
        canvas.drawOval(RectF(0f, 0f, bitmapWidth.toFloat(), bitmapHeight.toFloat()), paint)
        return dstBitmap
    }


    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawColor(Color.GREEN)
        val layerId =
            canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
        canvas.drawBitmap(dstBitmap, 0f, 0f, paint)
        paint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.SRC_IN))
        canvas.drawBitmap(
            srcBitmap,
            (dstBitmap.width * 1.0f) / 2,
            (dstBitmap.height * 1.0f) / 2,
            paint
        )
        paint.setXfermode(null)
        canvas.restoreToCount(layerId)
    }

}

从上面代码可以看到,在saveLayer()前,我们先将整个画布填充为了绿色,如下图


image.png

如果把saveLayer()去掉,效果图如下


image.png
可以看到这个效果是不正确的,这又是问什么呢?这就要从saveLayer()的作用说起。

1.2.1、saveLayer()的作用

saveLayer()函数被调用后,会生成一个全透明的画布,画布大小就是我们指定的RectF矩形区域的大小。在调用saveLayer()之后所有的绘图操作都是在这个新生成的画布上进行的。
而使用Xfermode进行图像混合时,在设置Xfermode之前,会把之前画布上所有的图像作为目标图像,而在调用saveLayer()时,新生成的画布上只有一个圆形图像,其他部分为透明的,所以除与圆形相交的区域都是空白像素。
如果没有调用saveLayer()的话,在设置Xfermode前,画布先是被填充了绿色,然后使用canvas.drawBitmap()生成了一个新的透明图层,在图层上绘制了一个黄色圆形。所以在绘制源图像时,目标图像是没有透明像素的,所以源图像也全部显示了。

1.2.2、saveLayer()和save()的区别

如果把上面例子的中的saveLayer()方法替换成了save()呢?
效果如下图

image.png
发现和去掉saveLayer()的效果完全一样。
这也说明了save()并没有新建画布,它只是保存当前画布的状态而已

2、画布与图层

3、saveLayer()和saveLayerAlpha()

3.1、saveLayer()

3.1、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,这个也很好理解,就是创建一个带有透明度的画布,取值范围[0~255]。

4、restore()和restoreToCount()

这两个函数针对的是同一个栈,无论是save()还是saveLayer()保存画布使用的都是同一个栈,所以完全可以通用,不同的是restore()默认退出栈顶的画布,还原状态,而restoreToCount(int count)则表示一直退栈,直到把指定的索引的画布退出即可。

上一篇 下一篇

猜你喜欢

热点阅读