美文网首页
Canvas与图层

Canvas与图层

作者: code希必地 | 来源:发表于2020-12-18 11:47 被阅读0次

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)
  • RectF bounds:要保存的区域所对应的矩形
  • 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表示保存全部内容。
    下面以Xfermode为例,来看看saveLayer()函数做了什么
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、画布与图层

  • 图层:每次调用Canvas.drawXXX()函数都会生成一个透明度层来绘制这个图形。
  • 画布:每块画布都是一个Bitmap,所有的图像都是画在这个Bitmap上的。而调用Canvas.drawXXX()会生成一个透明图层,然后在这个图层上绘制图形,绘制完成后,就覆盖在画布上。所以如果我们连续调用5次draw函数就会生成5个透明图层,画完之后依次覆盖在画布上显示。
    画布分为两种:一种是View的原始画布,通过onDraw(Canvas canvas)函数传入的,一种是人造画布,通过saveLayer()、new Canvas(Bitmap bitmap)等函数,人为的新建一个画布。尤其是saveLayer(),一旦调用,以后所有drawXXX()所画的图像都是在这个画布上,只有调用restore()、restoreToCount()之后,才会回到原画布上进行绘制。

3、saveLayer()和saveLayerAlpha()

3.1、saveLayer()

  • 1、saveLayer()会创建一个新的透明的画布,在调用saveLayer()后的任何对画布的操作都是针对新的画布的,并不会对之前的画布有影响。
  • 2、通过Rect()指定新建画布的大小
    有一点需要注意:我们应该按需指定新建画布的大小,并不需要每次都新建一块屏幕大小的画布,因为这样可能会造成不必要的内存浪费,而且还有可能造成内存溢出。比如:分辨率为1024X768的机器,一个像素占8个字节,则新建一个屏幕大小的画布所占内存为1024X768X8=6.2MB,所以在新建画布时,需要按需创建。

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)则表示一直退栈,直到把指定的索引的画布退出即可。

相关文章

  • Canvas与图层

    1、Canvas 1.1、save() 为了实现一些效果不得不对画布进行操作,但是操作完了,画布的状态也改变了,这...

  • canvas图层

    canvas理论上是不存在图层的概念,要实现图层可以通过在内存中建立多个canvas对象,通过globalComp...

  • 高级UI<第十四篇>:Paint基本使用

    编写自定义View必然会在onDraw(Canvas canvas)方法里面实现,Canvas是图层,用来显示图像...

  • Android高级进阶——绘图篇(七)Canvas 与 图层(一

    开篇 前面很多篇文章都用到了图层的概念,但是一直没有详细介绍,今天这篇文章将详细的介绍 Canvas 与 图层的概...

  • 小程序 canvas 自定义底部导航栏

    一、说明 小程序层级canvas最高,会有图表遮挡菜单栏的问题,这里写个canvas菜单看看能否调整图层,详细文档...

  • 4icon 的各种做法

    右击图层,然后复制图层,放在new文件夹里面哦 发现不是100乘100,又去image里面吧canvas size...

  • View绘制相关

    硬件加速和软件加速的区别 Canvas与图层 Bitmap 自定义style 硬件加速和软件加速的区别 硬件加速是...

  • Android画布Canvas--区域Region

    Canvas类有很多画图形的方法,除了常用的图形外,安卓还提供了Region--区域,表示Canvas图层上一块封...

  • canvas中常见的几种图形画法

    今天给大家分享几种canvas中常见的图形画法。//圆形context.beginPath();//新建图层con...

  • Canvas图层的概念(saveLayer)

    图层的概念的PhotoShop中比较常见,常常设计图标的时候当前图层的画布上只绘制背景,新增的图元习惯性的在新的图...

网友评论

      本文标题:Canvas与图层

      本文链接:https://www.haomeiwen.com/subject/gehxnktx.html