Android绘制图片的几种方式
本文是 Android 音视频开发系列基础篇的第一篇,学习和总结 Android 绘制一张图片的几种方式,如果要问 Android 平台绘制一张图片一共有几种方式,从目前的知识图谱里找到了以下六种,相信随着知识的积累,以后还会学习到别的方法,下面逐一探讨这几种方式。
- 使用 ImageView 绘制一张图片
利用 Android 系统提供的 UI 组件 ImageView,能很方便的绘制出一张图片,无论在 xml 布局文件里声明 ImageView 标签, 还是在代码里动态创建 ImageView, 添加到布局文件,两者的使用都非常简单,这里就不再赘述。
- 使用自定义 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 | } |
- 使用 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 | } |
- 使用自定义 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 | } |
- 使用 TextureView 绘制一张图片
TextureView 是一个结合了 View 和 SurfaceTexture 的 View 对象。既可用于显示图片,也用于显示视频流。
TextureView 和 SurfaceView 扮演的角色类似,且都是 View 层次结构的组成部分。不过,SurfaceView 和 TextureView 拥有截然不同的实现。与 SurfaceView 不同,TextureView 不会创建单独的窗口,而是充当常规 View。 这个关键区别允许对 TextureView 进行移动,变形,设置动画等。
使用 TextureView 很简单,获取到它的 SurfaceTexture,然后可以使用SurfaceTexture 渲染内容。以下代码展示了使用 TextureView 绘制图片的方法:
1 |
|
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 | } |
- 使用自定义 TextureView 绘制一张图片
通过自定义 TextureView 绘制图片的方法和使用 TextureView 绘制的方法基本一致,通过 TextureView.surfaceTextureListener,等到 SurfaceTexture 可用后使用它进行绘制。
Android 绘制图片的几种方式就介绍到这里,源码请移步:github-StreamTour