Android TexureView和SurfaceView

一、核心概念

  • SurfaceView :单独的 Surface,独立于 View 层级 ,由 SurfaceFlinger 直接合成,低延迟、性能好 ,但几乎不受 View 变换/裁剪影响

  • TextureView :在 View 层级里,用 SurfaceTexture 作为内容源,支持平移/缩放/旋转/透明/圆角/动画 ,但性能略差 、需要硬件加速

二、差异速览表

维度 SurfaceView TextureView
合成路径 独立 Surface → SurfaceFlinger 合成 作为普通 View 纹理参与 View 树合成
性能/延迟 ✅ 更低延迟、CPU/GPU 压力更小(适合视频/相机) ❌ 略高;受 View 合成影响
变换/动画 ❌ 基本不支持 scale/rotate/clip(位置可移动) ✅ 支持任意动画、透明、圆角、裁剪
硬件加速 不依赖(但底层通常硬件合成) 必须开启硬件加速
Z 顺序/遮挡 在很多机型上盖在普通 View 之上(或通过 setZOrder* 控制) 与普通 View 一样按层级排序
截图/录屏 不参与 View 的 draw();系统级录屏一般能抓到(但应用内截图抓不到它的内容) 参与 View 渲染,应用内截图/过渡动画可见
DRM/安全播放 更稳(部分设备/方案偏好 secure surface) 某些 L1 场景可能不被允许(看设备/DRM实现)
适合场景 视频播放、相机预览、直播、低延迟渲染 需要变换/蒙版/动效(相册封面、聊天室小窗)

注:从 Android 8.0 起,SurfaceView 的滚动/裁剪行为比老版本更"像"普通 View,但依旧不支持复杂变换。

三、选型建议(决策树)

  • 要做动效/缩放/旋转/圆角/矩形外裁剪? → 选 TextureView

  • 追求更低延迟/更省电/大面积视频/相机预览? → 选 SurfaceView

  • 需要安全播放(DRM)或遇到黑屏兼容问题? → 优先 SurfaceView

  • 要在 Compose 或复杂 View 动画中无缝参与? → 优先 TextureView

四、常用代码骨架

1)SurfaceView(典型视频/相机)

kotlin 复制代码
class VideoSurfaceView @JvmOverloads constructor(
    ctx: Context, attrs: AttributeSet? = null
) : SurfaceView(ctx, attrs), SurfaceHolder.Callback {

    private var player: MediaPlayer? = null // 或 ExoPlayer/MediaCodec

    init { holder.addCallback(this) }

    override fun surfaceCreated(h: SurfaceHolder) {
        // 1. 绑定 Surface
        player?.setSurface(h.surface)
        // 2. 开始/恢复播放
        player?.start()
    }

    override fun surfaceChanged(h: SurfaceHolder, format: Int, w: Int, h: Int) {
        // 可在此调整画面裁剪/拉伸策略(通过视频层面适配)
    }

    override fun surfaceDestroyed(h: SurfaceHolder) {
        // 解绑,避免野指针
        player?.setSurface(null)
    }
}

要点

  • 不要对 SurfaceView 做复杂动画;如需覆盖/悬浮,使用 setZOrderOnTop(true) 或 setZOrderMediaOverlay(true)(谨慎使用)。

  • XML/布局裁剪对它并不可靠;用视频端(渲染矩阵)处理比例。

2)TextureView(需要动画/裁剪)

kotlin 复制代码
class VideoTextureView @JvmOverloads constructor(
    ctx: Context, attrs: AttributeSet? = null
) : TextureView(ctx, attrs), TextureView.SurfaceTextureListener {

    private var player: MediaPlayer? = null // 或 ExoPlayer/MediaCodec

    init { surfaceTextureListener = this; isOpaque = true } // isOpaque=true 更省
    override fun onSurfaceTextureAvailable(st: SurfaceTexture, w: Int, h: Int) {
        player?.setSurface(Surface(st))
        player?.start()
    }
    override fun onSurfaceTextureSizeChanged(st: SurfaceTexture, w: Int, h: Int) { }
    override fun onSurfaceTextureDestroyed(st: SurfaceTexture): Boolean {
        player?.setSurface(null)
        return true // 我们不复用 st
    }
    override fun onSurfaceTextureUpdated(st: SurfaceTexture) { }
}

要点

  • 需要 硬件加速(确保 Activity/Window 未禁用)。
  • 可以通过 matrix 做缩放/平移适配填充(例如 CenterCrop):
scss 复制代码
fun TextureView.applyCenterCrop(videoW: Int, videoH: Int) {
    val viewW = width.toFloat(); val viewH = height.toFloat()
    val scale = maxOf(viewW / videoW, viewH / videoH)
    val scaledW = videoW * scale; val scaledH = videoH * scale
    val dx = (viewW - scaledW) / 2f; val dy = (viewH - scaledH) / 2f
    val m = Matrix().apply { setScale(scale, scale); postTranslate(dx, dy) }
    setTransform(m)
}

3)ExoPlayer / Media3 快速切换 Surface 类型

  • XML(StyledPlayerView)
xml 复制代码
<com.google.android.exoplayer2.ui.StyledPlayerView
    android:id="@+id/playerView"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    app:surface_type="surface_view" />  <!-- 或 "texture_view" -->
  • Kotlin(旧版 ExoPlayer API 示意)
arduino 复制代码
playerView.useController = false
// 切到 TextureView:player.setVideoTextureView(textureView)
// 切到 SurfaceView:player.setVideoSurfaceView(surfaceView)

具体方法名按你所用的 ExoPlayer/Media3 版本调整(VideoComponent 接口上有对应的 setVideoSurface*)。

五、常见坑 & 最佳实践

  1. 黑屏/花屏/绿屏
  • 多出现在 TextureView + 部分厂商机,可优先换 SurfaceView 试;或确保硬件加速打开。

  • MediaCodec 渲染到 Surface 的生命周期要对齐:注销前 setSurface(null)。

  1. 比例适配
  • SurfaceView:通过播放器/渲染端(例如 ExoPlayer AspectRatioFrameLayout)处理;不要指望父布局裁剪。

  • TextureView:用 setTransform(matrix) 控制裁剪/缩放,避免 shader 额外开销。

  1. 层级/遮挡
  • SurfaceView 通常盖在上层(或受 setZOrder* 影响),在其上叠盖 UI 可能被"穿透";确保使用 ...MediaOverlay,把弹幕/控件放在另一个窗口层级或改用 TextureView。
  1. 录屏/截图
  • 应用内对 View 的 draw() 并不会画出 SurfaceView 内容(看起来"截不到"),系统录屏一般可以。

  • DRM/FLAG_SECURE 场景可能禁止录屏/截图,优先 SurfaceView。

  1. 功耗与帧稳
  • 大面积、持续视频/相机 → SurfaceView 更省、更稳。

  • 小窗/动效/复杂裁剪 → TextureView 体验更好,但注意掉帧风险(降低分辨率或限制刷新)。

  1. Compose 中使用
  • 用 AndroidView(factory = { StyledPlayerView(...) });需要动效时把 app:surface_type 设置为 texture_view。

  • 仅自绘可用 SurfaceView 自定义 View 嵌入到 Compose。

六、简单结论

  • "能用 SurfaceView 就用 SurfaceView;要做动画/变换就用 TextureView。"
  • 视频/相机/直播 → SurfaceView(性能、时延、兼容性更稳)
  • 小窗预览、旋转/缩放、裁剪/圆角、复杂叠加 → TextureView
相关推荐
前端老鹰3 小时前
HTML `<datalist>`:原生下拉搜索框,无需 JS 也能实现联想功能
前端·css·html
code_YuJun3 小时前
脚手架架构设计
前端
pepedd8643 小时前
WebAssembly简单入门
前端·webassembly·trae
ze_juejin3 小时前
JavaScript 的基本数据类型
前端
喜葵3 小时前
前端安全防护深度实践:从XSS到CSRF的完整安全解决方案
前端·安全·xss
满分观察网友z4 小时前
JavaScript 算法探秘:如何优雅地打印一个“回文”数字金字塔(从入门到高阶)
前端
恋猫de小郭4 小时前
Flutter 真 3D 游戏引擎来了,flame_3d 了解一下
android·前端·flutter
陶甜也4 小时前
无需服务器,免费、快捷的一键部署前端 vue React代码--PinMe
服务器·前端·vue.js
烛阴4 小时前
TypeScript 进阶必修课:解锁强大的内置工具类型(二)
前端·javascript·typescript