今天就來實作 setRepeatMode 和 setShuffleMode 吧,看 API 時還蠻直覺的,就塞個數字進去就好了 XD,但在 UI 操作時,使用者其實是切換狀態,舉例來說:shuffle 模式支援開啟和關閉,repeat 模式支援關閉、開啟、一首重複播放,就來看看要怎麼實作吧!
這兩個按鈕就加在 NowPlaying 頁面,在這天 NowPlaying 滿版 UI (資訊顯示) 所提到的畫面,在上下一首隔壁加入這兩個功能,關於圖的部分,也是用內建的向量圖,在選擇 icon 的地方,打上 shuffle 和 repeat,就會顯示相關的 icon 了,repeat 還會有兩張圖,這兩張圖都需要,分別對應到不同的 repeat mode。
關於 xml 的部分也是用 constraint layout 的方式加入,可以平均分配位置,這邊就不特別敘述了。在 NowPlayingFragment 內加入 ClickListener,點擊會切換模式,那切換模式後 icon 要有變化,這樣使用者才會知道是切換了,這邊等下再來講 XD
shuffleButton.setOnClickListener {
nowPlayingViewModel.changeShuffleMode()
}
repeatButton.setOnClickListener {
nowPlayingViewModel.changeRepeatMode()
}
在 NowPlayingViewModel 加入對應的兩個 function,主要的概念也蠻直覺的,透過 mediaController 拿到目前的模式,然後設定下一個模式,再透過 transportControls 設定。
設定會用到的 Shuffle 模式 有這兩個,簡單來說是開和關 XD,還有另外兩個分別是:
Repeat 模式就支援三種,除了開和關外,REPEAT_MODE_ONE 指的是對單一首歌 repeat,就是會一直重複播這首歌。另外兩種沒用到的同 Shuffle,也是 group 和 invalid。
private val shuffleModeChoices =
listOf(PlaybackStateCompat.SHUFFLE_MODE_NONE, PlaybackStateCompat.SHUFFLE_MODE_ALL)
private val repeatModeChoices =
listOf(
PlaybackStateCompat.REPEAT_MODE_NONE,
PlaybackStateCompat.REPEAT_MODE_ONE,
PlaybackStateCompat.REPEAT_MODE_ALL
)
fun changeShuffleMode() {
val nowShuffleMode =
shuffleModeChoices.indexOf(musicServiceConnection.mediaController.shuffleMode)
val targetShuffleMode = when (nowShuffleMode) {
PlaybackStateCompat.SHUFFLE_MODE_NONE -> PlaybackStateCompat.SHUFFLE_MODE_ALL
PlaybackStateCompat.SHUFFLE_MODE_ALL -> PlaybackStateCompat.SHUFFLE_MODE_NONE
else -> PlaybackStateCompat.SHUFFLE_MODE_ALL
}
musicServiceConnection.transportControls.setShuffleMode(targetShuffleMode)
}
fun changeRepeatMode() {
val nowRepeatMode =
repeatModeChoices.indexOf(musicServiceConnection.mediaController.repeatMode)
val targetRepeatMode = when (nowRepeatMode) {
PlaybackStateCompat.REPEAT_MODE_NONE -> PlaybackStateCompat.REPEAT_MODE_ONE
PlaybackStateCompat.REPEAT_MODE_ONE -> PlaybackStateCompat.REPEAT_MODE_ALL
PlaybackStateCompat.REPEAT_MODE_ALL -> PlaybackStateCompat.REPEAT_MODE_NONE
else -> PlaybackStateCompat.REPEAT_MODE_ALL
}
musicServiceConnection.transportControls.setRepeatMode(targetRepeatMode)
}
在 MusicServiceConnection 內有 MediaControllerCallback,在這個 callback 內再去 override 兩個 function,這樣就可以知道 shuffle mode 和 repeat mode 的變化了,當模式有改動時,這邊就會收到通知,然後 NowPlayingViewModel 會觀察 shuffleModeState,NowPlayingFragment 再觀察就可以知道目前的模式,顯示出對應的 icon。
private inner class MediaControllerCallback : MediaControllerCompat.Callback() {
.....
override fun onShuffleModeChanged(shuffleMode: Int) {
super.onShuffleModeChanged(shuffleMode)
Log.d(TAG, "MediaControllerCallback onShuffleModeChanged:$shuffleMode")
shuffleModeState.postValue(shuffleMode)
}
override fun onRepeatModeChanged(repeatMode: Int) {
super.onRepeatModeChanged(repeatMode)
Log.d(TAG, "MediaControllerCallback onRepeatModeChanged:$repeatMode")
repeatModeState.postValue(repeatMode)
}
}
在 NowPlayingFragment 內設定顯示的邏輯
nowPlayingViewModel.shuffleMode.observe(viewLifecycleOwner,
Observer {
when (it) {
PlaybackStateCompat.SHUFFLE_MODE_NONE -> {
shuffleButton.setColorFilter(Color.BLACK)
}
PlaybackStateCompat.SHUFFLE_MODE_ALL -> {
shuffleButton.setColorFilter(ContextCompat.getColor(requireContext(),R.color.colorPrimary)
)
}
}
}
)
到這邊就大功告成了,可以注意到在 UI 頁面並不會管理狀態,UI 只負責切換指令和接收現在要顯示哪種模式,因為切換指令並不一定會從 UI 上觸發,Notification 或是 Widget 都是有可能的,不過這兩個地方還沒實作到 XD,如果要記錄上次的模式,這邊還需要實作 SharePreference 來記錄,就不會每次重開 App 都還要重新按。
成果圖:
Shuffle On
Repeat All
Repeat One
程式碼在這,分支名稱(day26_shuffle_repeat_mode): Fancy/day26_shuffle_repeat_mode