You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在ExoPlayer的PlayerView中用TextureView替代SurfaceView?求实现示例

Got it,我来帮你解决ExoPlayer中TextureView的使用问题,包括替换PlayerView里的SurfaceView以及完整实现示例!

一、替换PlayerView中的SurfaceView为TextureView

ExoPlayer的PlayerView默认使用SurfaceView,但我们可以通过自定义布局来替换成TextureView,步骤如下:

1. 自定义PlayerView布局

复制ExoPlayer官方的player_view.xml(可在ExoPlayer源码的res/layout目录找到),将其中的SurfaceView替换为TextureView,并保留原有控件ID(确保控制器等组件能正常关联)。示例布局custom_player_view.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- 替换SurfaceView为TextureView,保留原ID exo_content_frame -->
    <TextureView
        android:id="@id/exo_content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center" />

    <!-- 保留原有控制器视图 -->
    <ViewStub
        android:id="@id/exo_controller"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:inflatedId="@id/exo_controller"
        android:layout="@layout/exo_player_control_view" />

    <!-- 保留加载动画等其他组件 -->
    <ProgressBar
        android:id="@id/exo_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="gone" />
</merge>

2. 创建自定义PlayerView类

继承官方PlayerView,让它加载我们的自定义布局:

class CustomPlayerView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : PlayerView(context, attrs, defStyleAttr) {
    override fun getLayoutId(): Int {
        // 返回我们自定义的布局ID
        return R.layout.custom_player_view
    }
}

3. 在布局中使用自定义PlayerView

<com.yourpackage.CustomPlayerView
    android:id="@+id/custom_player_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

4. 初始化播放器

和使用普通PlayerView一样,直接关联ExoPlayer实例即可:

val customPlayerView = findViewById<CustomPlayerView>(R.id.custom_player_view)
val player = ExoPlayer.Builder(this).build()
customPlayerView.player = player

// 设置媒体源并播放
val mediaItem = MediaItem.fromUri("https://example.com/your-video-url.mp4")
player.setMediaItem(mediaItem)
player.prepare()
player.play()

二、完整的TextureView + ExoPlayer独立实现示例

如果你不想依赖PlayerView,直接用TextureView实现播放器,这里有一个完整的Kotlin示例:

1. 布局文件(activity_main.xml)

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextureView
        android:id="@+id/video_texture_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- 自定义播放控制按钮 -->
    <Button
        android:id="@+id/play_pause_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Play" />
</FrameLayout>

2. Activity代码

class MainActivity : AppCompatActivity(), TextureView.SurfaceTextureListener {
    private lateinit var exoPlayer: ExoPlayer
    private lateinit var textureView: TextureView
    private lateinit var playPauseBtn: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        textureView = findViewById(R.id.video_texture_view)
        playPauseBtn = findViewById(R.id.play_pause_btn)
        
        // 设置SurfaceTexture监听,确保Surface可用时再初始化播放器
        textureView.surfaceTextureListener = this

        // 初始化ExoPlayer实例
        exoPlayer = ExoPlayer.Builder(this).build()

        // 播放/暂停按钮逻辑
        playPauseBtn.setOnClickListener {
            if (exoPlayer.isPlaying) {
                exoPlayer.pause()
                playPauseBtn.text = "Play"
            } else {
                exoPlayer.play()
                playPauseBtn.text = "Pause"
            }
        }
    }

    override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
        // Surface可用,关联到ExoPlayer
        val surfaceHolder = Surface(surface)
        exoPlayer.setSurface(surfaceHolder)
        
        // 设置媒体源并准备播放
        val mediaItem = MediaItem.fromUri("https://example.com/your-video-url.mp4")
        exoPlayer.setMediaItem(mediaItem)
        exoPlayer.prepare()
    }

    override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
        // 可选:调整视频缩放模式,比如裁剪适配
        exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
    }

    override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
        // 销毁Surface时清理播放器关联
        exoPlayer.clearSurface()
        return true
    }

    override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
        // 可选:Surface更新回调,比如处理帧数据
    }

    override fun onDestroy() {
        super.onDestroy()
        // 释放播放器资源,避免内存泄漏
        exoPlayer.release()
    }
}

注意事项

  • TextureView相比SurfaceView更灵活,支持旋转、缩放等自定义变换,但在性能上略逊于SurfaceView,适合需要对视频画面做特殊处理的场景。
  • 务必在onDestroy中调用player.release(),避免内存泄漏。
  • 如果需要适配全屏、横竖屏切换,记得在生命周期回调中处理播放器的状态和Surface的重新关联。

内容的提问来源于stack exchange,提问作者Elnur Hajiyev

火山引擎 最新活动