Android绘制图片的几种方式

  本文是 Android 音视频开发系列基础篇的第一篇,学习和总结 Android 绘制一张图片的几种方式,如果要问 Android 平台绘制一张图片一共有几种方式,从目前的知识图谱里找到了以下六种,相信随着知识的积累,以后还会学习到别的方法,下面逐一探讨这几种方式。

  1. 使用 ImageView 绘制一张图片

  利用 Android 系统提供的 UI 组件 ImageView,能很方便的绘制出一张图片,无论在 xml 布局文件里声明 ImageView 标签, 还是在代码里动态创建 ImageView, 添加到布局文件,两者的使用都非常简单,这里就不再赘述。

  1. 使用自定义 View 绘制一张图片

  当 Android 系统提供的 UI 组件不能满足需求时,使用自定义 View 就有了用武之地,通过创建自己的 view 子类,可以精确控制屏幕元素的外观和功能。要使用自定义 view 绘制内容,需要四个基本组件:

​ –Bitmap,包含了要绘制内容的像素信息;

​ –Canvas,向 bitmap 里写入像素信息;

​ –绘图的图元(基本类型),例如一片矩形区域Rect,或者一个路径 Path,或者 text 文本、位图 Bitmap;

​ –Paint,描述要绘制内容的颜色和样式。

再简单一点的说 android.graphics 框架将绘制分为两个方面:

​ –绘制内容,由 Canvas 负责处理

​ –绘制方式,由 Paint 负责处理。

  例如,Canvas 提供绘制线条的方法,Paint 则提供定义线条颜色的方法。Canvas 具有绘制矩形的方法,Paint 则定义是在矩形中填充颜色还是留空。简而言之,Canvas 定义可以在屏幕上绘制的形状,Paint 则定义绘制的每个形状的颜色、样式和字体等。

  这里创建一个类 CustomView 继承 View,复写 onDraw 方法,onDraw() 的参数是 Canvas 对象,使用 Canvas 对象绘制自身。Canvas 类定义了绘制文本、线条、位图和许多其他图形的方法,调用这些绘制方法前,需要先创建 Paint 对象,实现起来非常简单,如果要调整绘制图片的大小,可以使用 Matrix 对象对图片进行缩放。示例代码如下:

1
class CustomView : View {
2
    private val mPaint = Paint()
3
    private lateinit var mBitmap: Bitmap
4
    private val mMatrix = Matrix()
5
6
    constructor(context: Context) : super(context) {
7
        initView(context)
8
    }
9
10
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
11
        initView(context)
12
    }
13
14
    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
15
        context,
16
        attrs,
17
        defStyleAttr
18
    ) {
19
        initView(context)
20
    }
21
22
    fun setBitmap(bitmap: Bitmap) {
23
        mBitmap = bitmap
24
    }
25
26
    private fun initView(context: Context) {
27
        mPaint.style = Paint.Style.STROKE
28
        mPaint.flags = Paint.ANTI_ALIAS_FLAG
29
        mPaint.isAntiAlias = true
30
    }
31
32
    override fun onDraw(canvas: Canvas) {
33
        mMatrix.setScale(0.5f, 0.5f)
34
        canvas.drawBitmap(mBitmap, mMatrix, mPaint)
35
    }
36
}
  1. 使用 SurfaceView 绘制一张图片

  SurfaceView 是android 系统提供的一个 UI 组件,有关 SurfaceView 的介绍,足够单独写一篇文章,这里通过简单的对比 SurfaceView 和 View,来建立一个对 SurfaceView 的初步认识:

​ –View在主线程中对页面进行刷新,而SurfaceView 则开启一个子线程来对页面进行刷新;

​ –View在绘图时没有实现双缓冲机制,SurfaceView 在底层机制中就实现了双缓冲机制;

​ –View适用于主动更新的情况,而 SurfaceView 适用于被动更新的情况,比如频繁刷新界面。

使用 SurfaceView 的时候,需要通过 SurfaceView 的 SurfaceHolder 注册一个接口回调,监听 SurfaceView 里Surface 的创建和销毁等操作,Surface 创建的时候 使用 Paint 和 Canvas 绘制图像,关键代码如下:

1
fun drawWithSurfaceView(surfaceView: SurfaceView) {
2
        surfaceView.setZOrderOnTop(true)
3
        val matrix = Matrix()
4
        val surfaceHolder = surfaceView.holder
5
        surfaceHolder.setFormat(PixelFormat.TRANSPARENT)
6
7
        surfaceHolder.addCallback(object : SurfaceHolder.Callback {
8
            override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) {}
9
10
            override fun surfaceDestroyed(surfaceHolder: SurfaceHolder?) {}
11
12
            override fun surfaceCreated(surfaceHolder: SurfaceHolder?) {
13
                if (surfaceHolder == null) {
14
                    return
15
                }
16
17
                val paint = Paint()
18
                paint.isAntiAlias = true
19
                paint.style = Paint.Style.STROKE
20
                paint.flags = Paint.ANTI_ALIAS_FLAG
21
22
                val canvas = surfaceHolder.lockCanvas()
23
                matrix.setScale(0.5f,0.5f)
24
                canvas.drawBitmap(getAssetsBitmap(), matrix, paint)
25
                surfaceHolder.unlockCanvasAndPost(canvas)
26
            }
27
        })
28
    }
29
30
    private fun getAssetsBitmap(): Bitmap {
31
        val assets = context.resources.assets
32
        val assetsInputStream = assets.open("hippo.jpg")
33
        return BitmapFactory.decodeStream(assetsInputStream)
34
    }
  1. 使用自定义 SurfaceView 绘制一张图片

  使用自定义 View 绘制图片和使用自定义 SurfaceView 绘制图片不同的地方在于,自定义View 在 onDraw 函数中执行绘制图像的操作,而自定义 SurfaceView 分别继承 SurfaceView 与 SurfaceHolder.Callback, 在函数 surfaceCreated 中执行绘制图片的操作:

1
class CustomSurfaceView : SurfaceView, SurfaceHolder.Callback {
2
    private var mCanvas: Canvas? = null
3
    private lateinit var mSurfaceHolder: SurfaceHolder
4
    private lateinit var mPaint: Paint
5
    private val mMatrix = Matrix()
6
7
    constructor(context: Context) : super(context) {
8
        initView(context)
9
    }
10
11
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
12
        initView(context)
13
    }
14
15
    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
16
        context,
17
        attrs,
18
        defStyleAttr
19
    ) {
20
        initView(context)
21
    }
22
23
    private fun initView(context: Context) {
24
        setZOrderOnTop(true)
25
        mSurfaceHolder = holder
26
        mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT)
27
        mSurfaceHolder.addCallback(this)
28
        
29
        mPaint = Paint()
30
        mPaint.style = Paint.Style.STROKE
31
        mPaint.flags = Paint.ANTI_ALIAS_FLAG
32
        mPaint.isAntiAlias = true
33
    }
34
35
    override fun surfaceCreated(surfaceHolder: SurfaceHolder?) {
36
        drawImage()
37
    }
38
39
    override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) {}
40
41
    override fun surfaceDestroyed(p0: SurfaceHolder?) {}
42
43
    private fun drawImage() {
44
        mCanvas = mSurfaceHolder.lockCanvas()
45
        mMatrix.setScale(0.5f, 0.5f)
46
        mCanvas?.drawBitmap(getBitmap(), mMatrix, mPaint)
47
        mSurfaceHolder.unlockCanvasAndPost(mCanvas)
48
    }
49
50
    private fun getBitmap(): Bitmap {
51
        val assets = context.resources.assets
52
        val assetsInputStream = assets.open("hippo.jpg")
53
        return BitmapFactory.decodeStream(assetsInputStream)
54
    }
55
}
  1. 使用 TextureView 绘制一张图片

  TextureView 是一个结合了 View 和 SurfaceTexture 的 View 对象。既可用于显示图片,也用于显示视频流。

  TextureView 和 SurfaceView 扮演的角色类似,且都是 View 层次结构的组成部分。不过,SurfaceView 和 TextureView 拥有截然不同的实现。与 SurfaceView 不同,TextureView 不会创建单独的窗口,而是充当常规 View。 这个关键区别允许对 TextureView 进行移动,变形,设置动画等。

  使用 TextureView 很简单,获取到它的 SurfaceTexture,然后可以使用SurfaceTexture 渲染内容。以下代码展示了使用 TextureView 绘制图片的方法:

1
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
2
fun withTextureView(textureView: TextureView) {
3
    val matrix = Matrix()
4
5
    textureView.surfaceTextureListener = object : SurfaceTextureListener{
6
        override fun onSurfaceTextureSizeChanged(p0: SurfaceTexture?, p1: Int, p2: Int) {
7
8
        }
9
10
        override fun onSurfaceTextureUpdated(p0: SurfaceTexture?) {
11
        }
12
13
        override fun onSurfaceTextureDestroyed(p0: SurfaceTexture?): Boolean {
14
            return true
15
        }
16
17
        override fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture, p1: Int, p2: Int) {
18
            val paint = Paint()
19
            paint.isAntiAlias = true
20
            paint.style = Paint.Style.STROKE
21
            paint.flags = Paint.ANTI_ALIAS_FLAG
22
23
            val canvas = textureView.lockCanvas()
24
            matrix.setScale(0.5f,0.5f)
25
            canvas.drawBitmap(getAssetsBitmap(), matrix, paint)
26
27
            textureView.unlockCanvasAndPost(canvas)
28
        }
29
    }
30
}
  1. 使用自定义 TextureView 绘制一张图片

  通过自定义 TextureView 绘制图片的方法和使用 TextureView 绘制的方法基本一致,通过 TextureView.surfaceTextureListener,等到 SurfaceTexture 可用后使用它进行绘制。

  Android 绘制图片的几种方式就介绍到这里,源码请移步:github-StreamTour