0%

Android-SurfaceView,GLSurfaceView,TextureView概述

概述

这篇文章介绍一下 SurfaceView, GLSurfaceView 以及 TextureView 的基础用法及一些区别。

  • SurfaceView: View 的子类,但不与宿主 Window 共享 Surface, 而是有自己独立的 Surface, 且可以在一个独立的线程中进行绘制,因此 SurfaceView 一般用来实现比较复杂的图像或动画/视频的显示。可以参考 Android双缓存与SurfaceView。由于其内容是绘制在一个独立的 Surface 上,因此无法用 scrollTo/By 等方法去移动操作 Canvas 里的内容,但是可对整个 View 进行平移,缩放,旋转等变换操作。
  • GLSurfaceView: 基于 SurfaceView 再次进行扩展,在 SurfaceView 基础上封装了 EGL 环境管理以及 Render 线程,专门为 OpenGl 显示渲染使用。参考 Android-OpenGL-ES笔记
  • TextrueView: Android 4.0 后引入 TextureView, 它将 SurfaceTexture 和 View 结合到了一起。与 SurfaceView 相比,它并没有创建一个单独的 Surface 来绘制,解决了 SurfaceView 无法在 Canvas 内容上做动画的问题。另外 TextureView 必须在硬件加速开启的窗口中使用。

SurfaceView

详细原理参考 Android双缓存与SurfaceView 一文。

SurfaceView 是一种较之 TextView, Button 等更为特殊的 View, 它不与其宿主的 Window 共享一个 Surface, 而是有自己的独立 Surface。并且它可以在一个独立的线程中绘制 UI。因此 SurfaceView 一般用来实现比较复杂的图像或动画/视频的显示。

一般来说,每个 Window 都有对应的 Surface 绘制表面,它在 SurfaceFlinger 服务中对应一个 Layer。而对于存在 SurfaceView 的 Window 来说,它除了自己的 Surface 以外还会有另一个 SurfaceView 独有的 Surface 绘制表面,在 SurfaceFlinger 服务中也会存在着两个 Layer 分别对应它们。查看 SurfaceView 的源码可以看到 SurfaceView 类似于 ViewRootImpl 一样其内部都有一个单独的 Surface 实例,结合 Android-Surface之创建流程及软硬件绘制 的解析,就能理解刚才对 SurfaceView 的描述了。

直接贴出一个使用 SurfaceView 的示例,示例会持续画出一个正弦函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
class MySurfaceView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : SurfaceView(context, attrs, defStyleAttr), SurfaceHolder.Callback, Runnable {

companion object {
private const val TAG = "MySurfaceView"
}

private val surfaceHolder: SurfaceHolder = holder
private var canvas: Canvas? = null

@Volatile
private var canDoDraw = false
private val drawThread = Thread(this)
private val lock = Object()

private var xx = 0f
private var yy = 400f
private val path = Path()
private val paint = Paint()

init {
surfaceHolder.addCallback(this)
isFocusable = true
isFocusableInTouchMode = true
keepScreenOn = true
path.moveTo(xx, yy)
paint.style = Paint.Style.STROKE
paint.strokeWidth = 10f
paint.color = Color.RED
drawThread.start()
}

override fun surfaceCreated(holder: SurfaceHolder) {
Log.d(TAG, "surfaceCreated")
setCanDraw(true)
}

override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
Log.d(TAG, "surfaceChanged")
}

override fun surfaceDestroyed(holder: SurfaceHolder) {
Log.d(TAG, "surfaceDestroyed")
canDoDraw = false
// 注意当APP到后台时会 destroy Surface, 回到前台会重新调用 surfaceCreated
// 因此这里不能移除回调,否则会黑屏
// surfaceHolder.removeCallback(this)
}

@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
Log.d(TAG, "onTouchEvent")
setCanDraw(!canDoDraw)
return super.onTouchEvent(event)
}

private fun setCanDraw(canDraw: Boolean) {
if (canDraw) {
synchronized(lock) {
try {
lock.notifyAll()
} catch (e: Exception) {
}
}
}
canDoDraw = canDraw
}

override fun run() {
while (true) {
synchronized(lock) {
if (!canDoDraw) {
try {
lock.wait()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
draw()
xx += 1
yy = (100 * sin(xx * 2 * Math.PI / 180) + 400).toFloat()
path.lineTo(xx, yy)
}
}

private fun draw() {
try {
canvas = surfaceHolder.lockCanvas()
canvas?.drawColor(Color.WHITE)
canvas?.drawPath(path, paint)
} catch (e: Exception) {
e.printStackTrace()
} finally {
surfaceHolder.unlockCanvasAndPost(canvas ?: return)
}
}
}

GLSurfaceView

参考 Android-OpenGL-ES笔记 一文。

GlSurfaceView 是一个 View, 它继承自 SurfaceView, 并增加了 Render 线程,它的作用就是专门为 OpenGl 显示渲染使用的。使用方法:

1.创建一个GlSurfaceView
2.为这个GlSurfaceView设置Renderer
3.在GlSurfaceView.renderer中绘制处理显示数据

首先需要在 AndroidManifest.xml 中设置 OpenGL ES 的版本:

1
2
<!-- Tell the system this app requires OpenGL ES 2.0. -->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />

使用 GLSurfaceView 作为主要视图的 Activity 的最少实现如下,只是绘制了一个绿色的背景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class MainActivity : AppCompatActivity() {
private lateinit var gLView: GLSurfaceView

public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gLView = MyGLSurfaceView(this)
setContentView(gLView)
}

private class MyGLSurfaceView(context: Context) : GLSurfaceView(context) {
private val renderer: MyGLRenderer

init {
// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2)
renderer = MyGLRenderer()
setRenderer(renderer)
// 该设置可防止系统在调用 requestRender() 之前重新绘制 GLSurfaceView 帧,更为高效。
// Render the view only when there is a change in the drawing data
renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
}
}

private class MyGLRenderer : GLSurfaceView.Renderer {
override fun onSurfaceCreated(unused: GL10, config: EGLConfig) {
// Set the background frame color
GLES20.glClearColor(0.0f, 255.0f, 0.0f, 1.0f)
}

override fun onDrawFrame(unused: GL10) {
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
}

override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
}
}
}

TextrueView

TextureView 是 Android 4 引入的组件,继承自 View, 用来承载数据流的显示,它将 SurfaceTexture 和 View 结合到了一起。与 SurfaceView 相比,它并没有创建一个单独的 Surface 来绘制,解决了 SurfaceView 无法在 Canvas 内容上做一些动画的问题。另外 TextureView 必须在硬件加速开启的窗口中使用。

SurfaceTexture 是 Surface 和 OpenGL ES 纹理相结合的产物,它作为 TextureView 的成员变量,是用于渲染内容的,它能捕获一个图像流的一帧来作为 OpenGL 的纹理(texture),这个图像流一般是来自相机的预览或视频的解码。和 SurfaceView 不同的是 SurfaceTexture 对图像流的处理并不直接显示,而是转为 GL 外部纹理,因此可用于图像流数据的二次处理(如 Camera 滤镜,特效等)。

对于 Camera 的预览数据,变成纹理后(可以做二次处理)可以交给 GLSurfaceView 直接显示,也可以通过 SurfaceTexture 交给 TextureView 作为 View 树中的一个硬件加速层来显示。

首先 SurfaceTexture 从图像流(来自 Camera 预览,视频解码, GL 绘制场景等)中获得帧数据,当调用 SurfaceTexture.updateTexImage() 时,根据内容流中最近的图像更新 SurfaceTexture 对应的 GL 纹理对象,接下来,就可以像操作普通 GL 纹理一样操作它了。

SurfaceTextureListener 是 TextureView 非常重要的监听回调,也可以说它是 TextureView 生命周期的体现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface SurfaceTextureListener {
// Invoked when a TextureView's SurfaceTexture is ready for use.
void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height);

// Invoked when the SurfaceTexture's buffers size changed.
void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height);

// Invoked when the specified SurfaceTexture is about to be destroyed.
// If returns true, no rendering should happen inside the surface texture after this method is invoked.
// If returns false, the client needs to call SurfaceTexture#release(). Most applications should return true.
boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface);

// Invoked when the specified SurfaceTexture is updated through SurfaceTexture#updateTexImage().
void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface);
}

使用 Camera 接口来简单看看通过 TextureView 实现相机预览功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class MainActivity : AppCompatActivity(), TextureView.SurfaceTextureListener {
private var textureView: TextureView? = null
private var camera: Camera? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
textureView = TextureView(this)
textureView?.surfaceTextureListener = this
setContentView(textureView)
}

override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
camera = Camera.open()
val previewSize: Camera.Size = camera?.parameters?.previewSize ?: return
textureView?.layoutParams = FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER
)
kotlin.runCatching {
camera?.setPreviewTexture(surface)
}
camera?.startPreview()
textureView?.rotation = 90.0f
}

override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
}

override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
camera?.stopPreview()
camera?.release()
return true
}

override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
}
}