You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ViewPager2+ExoPlayer多ViewHolder:切换页面视频未停止问题求助

解决ViewPager2中ExoPlayer切换页面/离开Activity时继续播放的问题

问题场景

使用ViewPager2实现图片/视频画廊,视频通过ExoPlayer播放,期望切换页面时自动停止前一个视频、播放当前页面的视频(若为视频项),但实际测试发现切换页面或退出Activity后,之前的视频仍在后台播放。

解决方案

1. 在Adapter中统一管理ExoPlayer实例

给Adapter添加集合,记录所有已创建的视频ViewHolder,方便统一控制播放器:

class ChatRoomGalleryAdapter(
    private val appContext: Context
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    // 新增:保存所有视频ViewHolder的引用
    private val videoViewHolders = mutableListOf<ChatRoomGalleryVideoViewHolder>()
    
    // ... 原有代码 ...
}

2. 修改VideoViewHolder,加入/移出管理集合

在VideoViewHolder初始化和回收时更新集合,同时提供播放、暂停、释放资源的方法:

class ChatRoomGalleryVideoViewHolder internal constructor(itemView: View) :
    RecyclerView.ViewHolder(itemView) {
    private val video: PlayerView = itemView.findViewById(R.id.galleryVideo)

    private val exoPlayer by lazy {
        ExoPlayer.Builder(itemView.context).apply {
            setSeekBackIncrementMs(5000)
            setSeekForwardIncrementMs(5000)
        }.build()
    }

    // 新增:初始化时加入管理集合
    init {
        (itemView.context as? YourActivity)?.viewAdapter?.videoViewHolders?.add(this)
    }

    fun bind(attachmentItem: AttachmentItem) {
        val file = File(attachmentItem.fetchFile())
        val mediaItem = MediaItem.fromUri(file.toUri(itemView.context)!!)
        
        video.apply {
            player = exoPlayer
            keepScreenOn = true
        }

        exoPlayer.apply {
            // 优化:仅当媒体项不同时才重新设置
            if (currentMediaItem?.mediaId != mediaItem.mediaId) {
                setMediaItem(mediaItem)
                prepare()
            }
            // 默认暂停,等待页面选中时再播放
            pause()
        }
    }

    // 新增:停止播放并释放资源
    fun stopAndRelease() {
        exoPlayer.stop()
        exoPlayer.release()
        video.player = null
    }

    // 新增:开始播放
    fun play() {
        exoPlayer.play()
    }

    companion object {
        fun from(parent: ViewGroup): ChatRoomGalleryVideoViewHolder =
            ChatRoomGalleryVideoViewHolder(
                LayoutInflater.from(parent.context)
                    .inflate(R.layout.chat_room_gallery_video_item, parent, false)
            )
    }
}

3. 处理ViewHolder回收,释放资源

在Adapter中重写onViewRecycled方法,回收视频ViewHolder时释放播放器:

override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
    super.onViewRecycled(holder)
    if (holder is ChatRoomGalleryVideoViewHolder) {
        holder.stopAndRelease()
        videoViewHolders.remove(holder)
    }
}

4. 页面切换时控制播放/暂停

修改Activity中的OnPageChangeCallback,切换页面时停止非当前位置的视频,播放当前页面的视频(若为视频项):

private val pageChangeCallback = object: ViewPager2.OnPageChangeCallback() {
    override fun onPageSelected(position: Int) {
        val attachmentItem = viewAdapter.getItemFromPosition(position) ?: return
        supportActionBar?.let {
            it.title = attachmentItem.toFullNameSender()
            it.subtitle = attachmentItem.date.formatAs(CHAT_ROOM_DATE_SEPARATOR, withUtc = false)
        }

        // 新增:控制视频播放状态
        viewAdapter.videoViewHolders.forEach { holder ->
            val holderPosition = holder.adapterPosition
            if (holderPosition == position) {
                holder.play()
            } else {
                holder.exoPlayer.pause()
            }
        }
    }
}

5. Activity销毁时释放所有播放器

在Activity的onDestroy中,释放所有视频播放器资源:

override fun onDestroy() {
    super.onDestroy()
    galleryViewPager.unregisterOnPageChangeCallback(pageChangeCallback)
    // 新增:释放所有播放器
    viewAdapter.videoViewHolders.forEach { it.stopAndRelease() }
    viewAdapter.videoViewHolders.clear()
}

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 19:15:44