iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 16
0
Mobile Development

Android 音樂播放器自己來系列 第 16

播放介面實作(2) - NowPlaying 小條 UI (資訊顯示)

  • 分享至 

  • xImage
  •  

今天來更新呈現的 NowPlaying 上的小條 UI,呈現播放資訊和進度,先介紹呈現播放資訊,裡面的實作還是參考 uamp。在 NowPlayingViewModel 傳入之前所實作的 MusicServiceConnection,裡面有播放歌曲的相關資訊,透過觀者的方式來取得資訊:

private val musicServiceConnection = musicServiceConnection.also {
        it.playbackState.observeForever(playbackStateObserver)
        it.nowPlaying.observeForever(mediaMetadataObserver)
        checkPlaybackPosition()
    }

private val playbackStateObserver = Observer<PlaybackStateCompat> {
        playbackState = it ?: EMPTY_PLAYBACK_STATE
        val metadata = musicServiceConnection.nowPlaying.value ?: NOTHING_PLAYING
        updateState(playbackState, metadata)
    }

private val mediaMetadataObserver = Observer<MediaMetadataCompat> {
        updateState(playbackState, it)
        mediaDuration = it.duration
    }

分別觀察 MusicServiceConnection 內的 MediaMetadataCompat(歌曲資訊) 和 PlaybackStateCompat (播放狀態),再有變化時,就會通知 updateState,來更新 NowPlayingMetadata,這個自訂的 NowPlayingMetadata 結構會被 NowPlayingFragment 觀察,真的是各種觀察,一層一層觀察上來 XD

val mediaMetadata = MutableLiveData<NowPlayingMetadata>()

......

private fun updateState(
        playbackState: PlaybackStateCompat,
        mediaMetadata: MediaMetadataCompat
    ) {
        // Only update media item once we have duration available
        if (mediaMetadata.duration != 0L && mediaMetadata.id != null) {
            val nowPlayingMetadata = NowPlayingMetadata(
                mediaMetadata.id!!,
                mediaMetadata.displayIconUri,
                mediaMetadata.title?.trim(),
                mediaMetadata.displaySubtitle?.trim(),
                NowPlayingMetadata.timestampToMSS(context, mediaMetadata.duration)
            )
            this.mediaMetadata.postValue(nowPlayingMetadata)
        }
    }

有了上述的資訊,在 NowPlayingFragment 再觀察 mediaMetadata,將結果更新在 UI 上

nowPlayingViewModel.mediaMetadata.observe(viewLifecycleOwner,
            Observer { mediaItem -> updateUI(view, mediaItem) })

更新的 function 就很一般,將要顯示的內容填入

private fun updateUI(view: View, metadata: NowPlayingViewModel.NowPlayingMetadata) {
        if (metadata.albumArtUri == Uri.EMPTY) {
            smallCover.setImageResource(R.drawable.ic_default_cover_icon)
            smallCover.setBackgroundResource(R.drawable.ic_default_cover_background)
        } else {
            Glide.with(view)
                .load(metadata.albumArtUri)
                .into(smallCover)
        }

        smallTitle.text = metadata.title
        smallSubTitle.text = metadata.subtitle
    }

另外一個要更新的部分為播放進度條,這邊用 SeekBar 元件來顯示,SeekBar 元件在顯示上有預設 padding,因此要撐滿左右要把 padding 設定為 0。

android:paddingStart="0dp"
android:paddingEnd="0dp"

然後因為在小條 UI 上,拖拉不太方便,因此把拖拉關掉,這邊好像沒有一個 API 可以直接使用,參考網路上的做法,自己處理 Touch 事件,回傳 true,消耗掉事件。

private fun disableSeekInSmallSeekBar() {
        smallSeekBar.setOnTouchListener { _, _ -> true }
    }

在上述的 musicServiceConnection 內,會呼叫 checkPlaybackPosition function,就是更新播放進度使用,因為播放進度在播放中會一直有變化,因此會使用 Handler 每間隔一段時間去抓取(0.1 秒),去抓取現在的播放進度,這邊會有兩個觀察者變數,一個是 mediaPlayProgress,目前播放進度的百分比,回傳 0~100,供 SeekBar 顯示使用。另外一個是 mediaPosition,回傳目前的播放時間,之後在大的顯示頁面會使用到。

/**
 * Internal function that recursively calls itself every [POSITION_UPDATE_INTERVAL_MILLIS] ms
 * to check the current playback position and updates the corresponding LiveData object when it
 * has changed.
 */
private fun checkPlaybackPosition(): Boolean = handler.postDelayed({
    val currPosition = playbackState.currentPlayBackPosition
    if (mediaPosition.value != currPosition) {
        mediaPosition.postValue(currPosition)
        if (mediaDuration > 0) {
            val progress = ((currPosition * 100 / mediaDuration)).toInt()
            mediaPlayProgress.postValue(progress.toInt())
        }
    }

    if (updatePosition)
        checkPlaybackPosition()
}, POSITION_UPDATE_INTERVAL_MILLIS)

覺得拿現在播放進度的時間在 uamp 專案內有點神奇,是需要運算出來的,不是直接拿某個欄位,裡面就會有播放時間了,這邊大家如果知道原因的話,或是有其他方式可以使用,歡迎在下面留言討論!

成果圖:

程式碼在這,分支名稱(day16_nowplaying_small_ui): Fancy/day16_nowplaying_small_ui


上一篇
播放介面實作(1) - NowPlaying 頁面
下一篇
播放介面實作(3) - NowPlaying 小條 UI (Play/Pause)
系列文
Android 音樂播放器自己來30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言