Android SurfaceView与TextureView使用方法详细讲解

来自:网络
时间:2022-12-26
阅读:
目录

Surface

官方对Surface的解释是:由屏幕合成器管理的原始缓冲区上的句柄,所谓原生缓冲器,是用于保存当前窗口的像素数据的,也就是说,通过Surface可以获取原生缓冲器以及其中的内容。Surface对应一块屏幕缓冲区,每个Window对应一个Surface,任何View都画在Surface上,Surface中的Canvas,是用于提供画图的地方。

SurfaceView

SurfaceView与普通的View不同,它拥有自己的Surface,它的工作方式是创建一个区别于应用窗口的新窗口,与宿主窗口分离,可以在单独线程中处理业务,不受View的属性控制,无法进行平移缩放等转换,它是通过“双缓冲”机制来达到高效的界面刷新效果。

双缓冲技术是把要处理的图片在内存中处理好之后,再将其显示在屏幕上。双缓冲主要是为了解决反复局部刷屏带来的闪烁。把要画的东西先画到一个内存区域里,然后整体的一次性画出来。

下面通过一个相机预览的功能,来看一下SurfaceView的具体使用

private lateinit var surfaceHolder: SurfaceHolder

获取摄像机管理类,打开某个具体的摄像机,在打开成功的回调里面,创建预览请求

        val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
        val surfaceView = findViewById<SurfaceView>(R.id.surfaceView)
        surfaceHolder = surfaceView.holder
        surfaceHolder.setKeepScreenOn(true)
        surfaceHolder.addCallback(object : SurfaceHolder.Callback {
            override fun surfaceCreated(p0: SurfaceHolder) {
                if (ActivityCompat.checkSelfPermission(
                        this@SurfaceActivity,
                        Manifest.permission.CAMERA
                    ) != PackageManager.PERMISSION_GRANTED
                ) {
                    //无相机权限
                    return
                }
                try {
                    //获取可用相机设备列表
                    val cameraIdList = cameraManager.cameraIdList
                    //打开相机
                    cameraManager.openCamera(
                        cameraIdList[0],
                        //摄像头创建监听
                        object : CameraDevice.StateCallback() {
                            override fun onOpened(p0: CameraDevice) { //打开摄像头
                                try {
                                    startPreview(p0)
                                } catch (e: CameraAccessException) {
                                    e.printStackTrace()
                                }
                            }
                            override fun onDisconnected(p0: CameraDevice) { //关闭摄像头
                                p0.close()
                            }
                            override fun onError(p0: CameraDevice, p1: Int) {
                                Log.i(tag, "CameraDevice.StateCallback onError")
                            }
                        },
                        null
                    )
                } catch (e: CameraAccessException) {
                    e.printStackTrace()
                }
            }
            override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) {
                Log.i(tag, "surfaceChanged")
            }
            override fun surfaceDestroyed(p0: SurfaceHolder) {
                Log.i(tag, "surfaceDestroyed")
            }
        })

开始预览

    private fun startPreview(cameraDevice: CameraDevice) {
        val captureRequest = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
        captureRequest.addTarget(surfaceHolder.surface)
        cameraDevice.createCaptureSession(
            listOf(surfaceHolder.surface),
            //会话状态回调
            object : CameraCaptureSession.StateCallback() {
                override fun onConfigured(p0: CameraCaptureSession) {
                    try {
                        // 创建预览需要的CaptureRequest.Builder
                        val previewRequestBuilder =
                            cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
                        previewRequestBuilder.addTarget(surfaceHolder.surface)
                        val previewRequest = previewRequestBuilder.build()
                        p0.setRepeatingRequest(previewRequest, null, null)
                    } catch (e: CameraAccessException) {
                        e.printStackTrace()
                    }
                }
                override fun onConfigureFailed(p0: CameraCaptureSession) {
                    Log.i(tag, "onConfigureFailed")
                }
            }, null
        )
    }

这样,简易的相机预览功能就出来啦,当然,我们还需要申请CAMERA权限,这里略去。

TextureView

TextureView可以说是一个结合了View和SurfaceTexture的View对象,一个可以把内容流作为外部纹理输出在上面的 View,它只能用于开启了硬件加速的窗口,TextureView不会创建一个独立的窗口,而是像普通的View一样,可以进行平移、旋转等动画,TextureView在Andriod 4.0之后的 API 中才能使用,不过现在的安卓设备基本不存在Andriod 4.0之前的版本了,所以这点不必在意。

TextureView的使用也比较简单,需要获取到它的SurfaceTexture,然后就可以以此来渲染了,下面通过一个简易的视频播放的示例,来瞧瞧它是怎么使用的。

        val textureView = findViewById<TextureView>(R.id.textureView)
        val mediaPlayer = MediaPlayer()
        textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
            override fun onSurfaceTextureAvailable(p0: SurfaceTexture, p1: Int, p2: Int) {
                with(mediaPlayer) {
                    setDataSource(GlobalData.videoUrl)
                    setSurface(Surface(p0))
                    prepare()
                    start()
                    Log.i(tag, "setOnPreparedListener")
                    setOnPreparedListener {
                        it.start()
                    }
                }
            }
            override fun onSurfaceTextureSizeChanged(p0: SurfaceTexture, p1: Int, p2: Int) {
            }
            override fun onSurfaceTextureDestroyed(p0: SurfaceTexture): Boolean {
                mediaPlayer.stop()
                mediaPlayer.release()
                return true
            }
            override fun onSurfaceTextureUpdated(p0: SurfaceTexture) {
            }
        }

SurfaceTexture

SurfaceTexture是Surface和OpenGL ES纹理的组合,用于提供输出到OpenGL ES纹理的Surface,和SurfaceView不同的是,它对图像流的处理并不直接显示,而是转为GL外部纹理,因此可用于图像流数据的二次处理,如Camera滤镜,桌面特效等,但是这样会有若干帧的延迟。同时,由于它本身管理BufferQueue,因此内存消耗也会稍微大一些。比如Camera的预览数据,变成纹理后就可以通过SurfaceTexture交给TextureView作为View层级中的一个硬件加速层来显示。

SurfaceView和TextureView的区别

  • SurfaceView是直接输出的,拥有自己独立Surface,它的渲染可以放在单独线程,其缺点是不能做变形和动画。
  • TextureView是一个可以把内容流作为外部纹理输出的,本身必须是一个硬件加速层,显示画面更新时有1~3帧的延迟。
  • TextureView本身也包含了SurfaceTexture,它与SurfaceView+SurfaceTexture组合相比,也可以把内容流上的图像转成纹理输出,区别在于TextureView是在View层级中绘制的,而SurfaceView+SurfaceTexture在单独的Surface上做绘制的。
SurfaceViewTextureView
占用内存低占用内存高
耗电低耗电高
绘制及时1-3帧延时
不支持动画截图支持动画截图

个人觉得,对于需要不断更新画布的游戏来说,SurfaceView是最好的选择,对于视频播放器或相机应用的开发,则TextureView更加适合。

返回顶部
顶部