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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android自定义View是指基于Android原生控件的一种扩展,可以根据自己的需求和设计规范来创建更加个性化和独特的控件。而歌词控件是一种针对音乐播放器或者视频播放器等应用场景中的需求,用于显示音乐或者视频的歌词的控件。 Android自定义View歌词控件的实现思路如下: 1. 首先需要自定义一个View,并继承自View或者其子类,如TextView。 2. 在自定义View中重写onDraw方法,在其中实现绘制歌词的逻辑。 3. 在onDraw方法中,使用Canvas对象进行绘制,可以使用drawText方法绘制歌词文本,也可以使用drawBitmap方法绘制图片背景等。 4. 可以通过自定义属性,如字体大小、字体颜色、歌词滚动速度等,来对歌词控件进行配置。 5. 如果需要实现歌词的滚动效果,可以使用ValueAnimator或者Scroller来实现歌词的平滑滚动。 6. 如果需要实现点击歌词跳转播放进度的功能,可以通过添加点击事件监听器,在触摸事件中判断点击位置对应的歌词行,并根据歌词的时间戳跳转到指定的播放进度。 总结来说,Android自定义View歌词控件的实现需要重写onDraw方法进行绘制,可以通过Canvas对象进行绘制文本或者图像,通过自定义属性进行配置,使用动或者滚动实现歌词的平滑滚动,通过监听触摸事件实现点击歌词跳转播放进度的功能。通过以上步骤,我们可以创建一个个性化的歌词控件,满足不同应用场景的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值