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
相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端