Android自定义view(三):Canvas绘制图片

一、drawBitmap

Bitmap是我们Android开发者最熟悉有陌生的老朋友了。它是很多内存问题的万恶之源,但我们又常常不用去碰它,而是把关于图片的操作交给Glide之类的框架。这里不详细讲解关于Bitmap的知识,只讲如何在Canvas里绘制它。
照例看一下Canvas里关于绘制Bitmap的方法:

  • drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
  • drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
  • drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
  • drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)

前两个方法只有dst参数类型一个是Rect一个是RectF的区别,都是把Bitmap的局部或这全部(由src参数确定)绘制到dst参数指定的区域内,可能会进行拉伸或者缩放。
这里随便照一张图片,放在相应的drawable目录内: 在这里插入图片描述
看如下示例:

private val paint = Paint()

private val bitmap = BitmapFactory.decodeResource(resources, R.drawable.poppy)
private val srcRect = Rect(0, 0, 1000, 800)
private val destRect = Rect(0, 0, 200, 500)

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    // 假定View的宽高为500px
    setMeasuredDimension(500, 500)
}

override fun onDraw(viewCanvas: Canvas) {
    super.onDraw(viewCanvas)
    // 给View一个背景颜色
    viewCanvas.drawColor(Color.CYAN)

    viewCanvas.drawBitmap(bitmap, srcRect, destRect, paint)
}

也就是要把下面红框选中部分绘制在画布(0, 0)和(200, 500)所确定的矩形内。由于两个矩形长宽比例不一致
在这里插入图片描述
效果图:
在这里插入图片描述
第三个方法则是从把(left, top)作为Bitmap的左上角开始绘制,不缩放。

private val paint = Paint()

private val bitmap = BitmapFactory.decodeResource(resources, R.drawable.poppy)

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    // 假定View的宽高为500px
    setMeasuredDimension(500, 500)
}

override fun onDraw(viewCanvas: Canvas) {
    super.onDraw(viewCanvas)
    // 给View一个背景颜色
    viewCanvas.drawColor(Color.CYAN)

    viewCanvas.drawBitmap(bitmap, 50f, 50f, paint)
}

效果:
在这里插入图片描述
至于第四个方法,等聊到Matrix类的时候再详细讲,此处暂时略过。

二、Canvas.drawPicture

关于Picture,官方文档描述如下:
A Picture records drawing calls (via the canvas returned by beginRecording) and can then play them back into Canvas (via Picture#draw(Canvas) or Canvas#drawPicture(Picture)).For most content (e.g. text, lines, rectangles), drawing a sequence from a picture can be faster than the equivalent API calls, since the picture performs its playback without incurring any method-call overhead.
大意是说,如果我们需要重复N次某些绘制操作,那么我们可以把这些绘制操作记录在Picture中,然后通过调用N次Picture.draw或者Canvas.drawPicture来实现。对于大多数内容(例如文本,线条,矩形),使用Picture要比每次都挨个去调用drawLine、drawText效率要高,因为少了很多方法调用的开销。
Picture的基本用法也比较简单,在beginRecording和endRecording方法调用之间通过返回的Canvas对象执行绘制操作即可:

val picture = Picture()
//使用500*500的画布
val pictureCanvas = picture.beginRecording(500, 500)

// 此处一顿乱draw
pictureCanvas.drawOval(...)
pictureCanvas.drawLines(...)
pictureCanvas.drawXXX(...)
...

picture.endRecording()
targetCanvas.drawPicture(picture)
//或者使用下面的方法
//picture.draw(targetCanvas)

需要注意的是,endRecording调用之后,不要再去操作pictureCanvas进行内容绘制了。
Canvas里用于绘制Picture的方法如下:

  • drawPicture(Picture picture)
  • drawPicture(Picture picture, RectF dst)
  • drawPicture(Picture picture, Rect dst)

第一个方法用于在画布当前位置开始绘制Picture,不缩放。看如下示例:

private val paint = Paint().apply {
    color = Color.BLACK
    style = Paint.Style.FILL
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    // 假定View的宽高为500px
    setMeasuredDimension(500, 500)
}

override fun onDraw(viewCanvas: Canvas) {
    super.onDraw(viewCanvas)
    // 给View一个背景颜色
    viewCanvas.drawColor(Color.CYAN)

    //将画布平移
    viewCanvas.translate(50f, 50f)

    //原则上不要在onDraw的时候new对象,这里是demo为了省事,大家别学我
    val picture = Picture()
    val pictureCanvas = picture.beginRecording(500, 500)
    pictureCanvas.drawCircle(250f, 250f, 250f, paint)

    picture.endRecording()
    viewCanvas.drawPicture(picture)
}

其中viewCanvas.translate(50f, 50f)是将画布平移至(50,50),后续讲画布操作会讲到。平移后,画布“当前位置”为(50,50)。所以效果如下:
在这里插入图片描述
后两个方法与Bitmap绘制类似,也可能缩放变形。由于创建Picture画布时已经指定了宽高,也就不需要指定src参数了。示例:

private val paint = Paint().apply {
    color = Color.BLACK
    style = Paint.Style.FILL
}
private val dst = Rect(100, 100, 300, 200)

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    // 假定View的宽高为500px
    setMeasuredDimension(500, 500)
}

override fun onDraw(viewCanvas: Canvas) {
    super.onDraw(viewCanvas)
    // 给View一个背景颜色
    viewCanvas.drawColor(Color.CYAN)

    //将画布平移
    viewCanvas.translate(50f, 50f)

    //原则上不要在onDraw的时候new对象,这里为了省事,大家别学我
    val picture = Picture()
    val pictureCanvas = picture.beginRecording(500, 500)
    pictureCanvas.drawCircle(250f, 250f, 250f, paint)

    picture.endRecording()
    viewCanvas.drawPicture(picture, dst)
}

效果:

三、Drawable

Drawable,可绘制对象。Canvas里并没有类似drawDrawable的方法,但是Drawable类提供了draw(Canvas canvas)方法。需要注意的是,绘制之前需要先Drawable的setBounds方法设置边界。
定义一个drawable的xml文件:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#FF8888" />
    <size android:width="100dp" android:height="50dp"/>
</shape>

绘制:

private val dst = Rect(100, 100, 300, 200)
    private val oval = resources.getDrawable(R.drawable.oval)

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // 假定View的宽高为500px
        setMeasuredDimension(500, 500)
    }

    override fun onDraw(viewCanvas: Canvas) {
        super.onDraw(viewCanvas)
        // 给View一个背景颜色
        viewCanvas.drawColor(Color.CYAN)

        oval.bounds = dst
        oval.draw(viewCanvas)
    }

效果:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值