如何在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
相关产品推荐
相关产品推荐

