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
相关推荐
king王一帅4 小时前
Incremark Solid 版本上线:Vue/React/Svelte/Solid 四大框架,统一体验
前端·javascript·人工智能
C雨后彩虹6 小时前
任务最优调度
java·数据结构·算法·华为·面试
智航GIS9 小时前
10.4 Selenium:Web 自动化测试框架
前端·python·selenium·测试工具
前端工作日常9 小时前
我学习到的A2UI概念
前端
徐同保9 小时前
为什么修改 .gitignore 后还能提交
前端
一只小bit9 小时前
Qt 常用控件详解:按钮类 / 显示类 / 输入类属性、信号与实战示例
前端·c++·qt·gui
Mr -老鬼10 小时前
前端静态路由与动态路由:全维度总结与实践指南
前端
颜酱10 小时前
前端必备动态规划的10道经典题目
前端·后端·算法
Chan1610 小时前
【 Java八股文面试 | JavaSE篇 】
java·jvm·spring boot·面试·java-ee·八股
wen__xvn10 小时前
代码随想录算法训练营DAY10第五章 栈与队列part01
java·前端·算法