如何在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




