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

如何在Media3 PlayerView中实现上一首/下一首歌曲播放功能

实现Media3 PlayerView上一首/下一首歌曲切换

1. 共享播放状态与歌曲列表

用ViewModel维护跨Fragment的播放状态,确保首页和详情页能同步歌曲列表与当前播放索引:

class AudioPlayerViewModel : ViewModel() {
    // 存储歌曲数据(Song是自定义数据类,包含uri、标题等字段)
    val songList = mutableStateListOf<Song>()
    // 当前播放歌曲的索引
    var currentPlayIndex by mutableStateOf(0)

    // 示例:从本地媒体库加载歌曲的方法
    fun loadLocalSongs(context: Context) {
        // 这里实现加载逻辑,填充songList
    }
}

2. 初始化ExoPlayer并绑定歌曲列表

在详情Fragment中,把歌曲列表转为MediaItem集合交给ExoPlayer,这是默认按钮生效的基础:

private lateinit var player: ExoPlayer
private lateinit var viewModel: AudioPlayerViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel = ViewModelProvider(requireActivity())[AudioPlayerViewModel::class.java]
    
    // 初始化ExoPlayer
    player = ExoPlayer.Builder(requireContext()).build()
    playerView.player = player

    // 将歌曲列表转为MediaItem并设置给Player
    val mediaItems = viewModel.songList.map { MediaItem.fromUri(it.uri) }
    player.setMediaItems(mediaItems)
    player.prepare()
    
    // 跳转到用户点击的歌曲位置
    player.seekTo(viewModel.currentPlayIndex, 0)
    player.play()
}

3. 启用默认按钮的切换功能

PlayerView的上一首/下一首按钮默认会调用Player的previous()和next()方法,只需配置重复模式并同步索引即可:

// 设置列表循环(可选,根据需求设置REPEAT_MODE_NONE/ONE/ALL)
player.repeatMode = Player.REPEAT_MODE_ALL

// 监听播放项切换,更新ViewModel中的索引
player.addListener(object : Player.Listener {
    override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
        super.onMediaItemTransition(mediaItem, reason)
        viewModel.currentPlayIndex = player.currentMediaItemIndex
    }
})

4. 自定义切换逻辑(可选)

如果需要特殊规则(比如跳过某些歌曲、自定义循环逻辑),可以手动拦截按钮点击:

// 获取上一首按钮并设置点击事件
val prevBtn = playerView.findViewById<ImageButton>(com.google.android.exoplayer2.ui.R.id.exo_prev)
prevBtn.setOnClickListener {
    val newIndex = if (viewModel.currentPlayIndex == 0) {
        viewModel.songList.size - 1 // 跳到最后一首
    } else {
        viewModel.currentPlayIndex - 1
    }
    updatePlayPosition(newIndex)
}

// 获取下一首按钮并设置点击事件
val nextBtn = playerView.findViewById<ImageButton>(com.google.android.exoplayer2.ui.R.id.exo_next)
nextBtn.setOnClickListener {
    val newIndex = if (viewModel.currentPlayIndex == viewModel.songList.size - 1) {
        0 // 跳到第一首
    } else {
        viewModel.currentPlayIndex + 1
    }
    updatePlayPosition(newIndex)
}

// 统一更新播放位置的方法
private fun updatePlayPosition(index: Int) {
    viewModel.currentPlayIndex = index
    player.seekTo(index, 0)
    player.play()
}

5. 首页与详情页的索引同步

首页RecyclerView点击歌曲时,更新ViewModel索引并跳转详情页:

// 首页RecyclerView的点击回调
adapter.setOnItemClickListener { _, position ->
    viewModel.currentPlayIndex = position
    findNavController().navigate(R.id.action_home_to_detail)
}

关键注意事项

  • 销毁Fragment时务必释放Player资源,避免内存泄漏:
override fun onDestroyView() {
    super.onDestroyView()
    playerView.player = null
    player.release()
}
  • 如果歌曲列表动态更新,需要重新调用player.setMediaItems()并刷新播放状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 03:24:51