Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jetpack Compose の動画表示対応 をまとめてみた

Avatar for nagayan nagayan
September 01, 2023

Jetpack Compose の動画表示対応 をまとめてみた

Android Jetpack Compose で動画表示対応 をまとめました。

Avatar for nagayan

nagayan

September 01, 2023
Tweet

Other Decks in Programming

Transcript

  1. (C) Gunosy Inc. All Rights Reserved. PAGE | 2 ▪

    名前:nagayan / 永山 雄 ▪ 所属:株式会社Gunosy – Android エンジニア auサービスToday を担当 ▪ X:@nagayan_dev 自己紹介
  2. (C) Gunosy Inc. All Rights Reserved. PAGE | 3 アジェンダ

    背景・方針 Jetpack Compose の動画実装 • Exoplayer、AndroidViewを使った、基本実装 • デフォルトの動画パーツを OFF にする • VideoStateの定義と実装 • 動画パーツの実装 • 動画の進捗とシークバーを連動させる • ライフサイクルで再生・停止・解放 まとめ
  3. (C) Gunosy Inc. All Rights Reserved. PAGE | 4 ▪

    auサービスTodayでは Compose 実装への置き換えを随時進めて います ▪ Jetpack Compose の動画実装を調べていましたが、情報が散見 されていました ▪ 動画部分のCompose化をしたため、その対応をまとめました ▪ 既存のデザインを踏襲するため、デフォルトの動画パーツは使用し ない方針です 背景・方針
  4. (C) Gunosy Inc. All Rights Reserved. PAGE | 5 ▪

    Android Jetpack Compose で動画表示 – https://tech.gunosy.io/entry/android_compose_video_player 今回の内容
  5. (C) Gunosy Inc. All Rights Reserved. PAGE | 7 dependencies

    { implementation "androidx.media3:media3-exoplayer:1.1.1" 使用するライブラリ Jetpack Compose の動画実装
  6. (C) Gunosy Inc. All Rights Reserved. PAGE | 8 val

    exoPlayer = remember(context) { ExoPlayer.Builder(context).build().apply { setMediaItem(MediaItem.fromUri(Uri.parse(url))) prepare() seekTo(0L) } } Exoplayer、AndroidViewを使った、基本実装 Jetpack Compose の動画実装 ▪ 動画を再生する Exoplayerを 作成します
  7. (C) Gunosy Inc. All Rights Reserved. PAGE | 9 Exoplayer、AndroidViewを使った、基本実装

    Jetpack Compose の動画実装 ▪ 作成したExoplayerを、 AndroidView の PlayerView に設定します val exoPlayer = remember(context) ~~~ Box(~~~) { AndroidView( modifier = ~~~, factory = { context -> PlayerView(context).apply { this.player = exoPlayer } })}
  8. (C) Gunosy Inc. All Rights Reserved. PAGE | 10 パーツがデフォルトのもの

      →パーツを自前実装する Jetpack Compose の動画実装 Exoplayer、AndroidViewを使った、基本実装
  9. (C) Gunosy Inc. All Rights Reserved. PAGE | 11 val

    exoPlayer = remember(context) ~~~ Box(~~~) { AndroidView( modifier = ~~~, factory = { context -> PlayerView(context).apply { useController = false this.player = exoPlayer } })} デフォルトの動画パーツを OFF にする Jetpack Compose の動画実装 ▪ PlayerView の useController を false に指定することで、デ フォルトの動画パーツを非表 示にできます
  10. (C) Gunosy Inc. All Rights Reserved. PAGE | 12 ▪

    UIを更新するため、独自の VideoState を定義します ▪ remember で State を保持します VideoStateの定義と実装 Jetpack Compose の動画実装 var videoState by remember { mutableStateOf(VideoState.LOADING) } enum class VideoState { LOADING, PAUSE, START, ENDED }
  11. (C) Gunosy Inc. All Rights Reserved. PAGE | 13 ▪

    onPlaybackStateChan ged で VideoState の 更新をします。 VideoStateの定義と実装 Jetpack Compose の動画実装 exoPlayer.addListener(object : Player.Listener { override fun onPlaybackStateChanged(playbackState: Int) { videoState = when { playbackState == Player.STATE_READY && playWhenReady -> VideoState.START playbackState == Player.STATE_ENDED && playWhenReady -> VideoState.ENDED ~~~ -> VideoState.LOADING ~~~ --> VideoState.PAUSE
  12. (C) Gunosy Inc. All Rights Reserved. PAGE | ▪ 再生停止ボタンを実装します

    ▪ 先ほどの State を元に、Start と Pause を切り替えます 14 動画パーツの実装 Jetpack Compose の動画実装 var videoState by remember { ~~~ } ~~~ if (videoState == VideoState.START) { Image( painter = ~中断ボタン画像~, contentDescription = ~~~, ) } else { Image( painter = ~再生ボタン画像~, contentDescription = ~~~, ) } ~~~
  13. (C) Gunosy Inc. All Rights Reserved. PAGE | 15 動画パーツの実装

    Jetpack Compose の動画実装 var currentTime by remember { mutableStateOf(0L) } ~~~ Slider( ~~~ value = currentTime.toFloat(), valueRange = 0f..exoPlayer.duration.~~~, colors = SliderDefaults.colors( ~~~ ))} ▪ シークバーの実装です ▪ Slider を用いて実装します ▪ 動画の進捗度として currentTime を定義します ▪ Slider の value に指定し、 valueRange は 0 から 動画 の長さを設定します
  14. (C) Gunosy Inc. All Rights Reserved. PAGE | 16 ▪

    シークバーの操作を反映させ ます ▪ onValueChange はシーク バー操作時に通知される Callback で、ここで通知され た値を currentTime に設定し ます 動画の進捗とシークバーを連動させる Jetpack Compose の動画実装 var currentTime by remember { mutableStateOf(0L) } ~~~ Slider( ~~~ value = currentTime.toFloat(), onValueChange = { timeMs -> val time = timeMs.toLong() exoPlayer.seekTo(time) currentTime = time }, valueRange = 0f..exoPlayer.duration.~~~, ~~~
  15. (C) Gunosy Inc. All Rights Reserved. PAGE | 17 ▪

    動画が再生されてても、シーク バーが動いてくれません ▪ 再生状態の時に LaunchedEffect 内で repeat をし、動画時間を取得して currentTime に反映させます。 動画の進捗とシークバーを連動させる Jetpack Compose の動画実装 var currentTime by remember { mutableStateOf(0L) } ~~~ if (videoState == VideoState.START) { LaunchedEffect(Unit) { repeat(Int.MAX_VALUE) { delay(1000) currentTime = exoPlayer.contentPosition }}}
  16. (C) Gunosy Inc. All Rights Reserved. PAGE | 18 ▪

    アプリがバックグラウンドに行っ た時に再生しっぱなしになってし まう ▪ DisposableEffect を用いて、ラ イフサイクルに合わせて処理を 実行 – onResume:再生 – onPause:停止 ライフサイクルで再生・停止・解放 Jetpack Compose の動画実装 DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { if (videoState == VideoState.START) { exoPlayer.play() } } else if (event == Lifecycle.Event.ON_PAUSE) { exoPlayer.pause() } } lifecycleOwner.lifecycle.addObserver(observer) ~~~
  17. (C) Gunosy Inc. All Rights Reserved. PAGE | 19 ▪

    DisposableEffect の onDispose で解放処理を実行 します ライフサイクルで再生・停止・解放 Jetpack Compose の動画実装 DisposableEffect(lifecycleOwner) { ~~~ onDispose { exoPlayer.run { playWhenReady = false ~~~ release() } lifecycleOwner.lifecycle.removeObserver(observer) } }
  18. (C) Gunosy Inc. All Rights Reserved. PAGE | 21 ▪

    Jetpack Compose で基本的な動画表示の実装について紹介しま した ▪ デフォルトの動画パーツを表示するだけなら簡単に実装できます が、カスタマイズしたパーツを表示するとなるとちょっと工夫が必要 になります ▪ 動画再生が一定時間経過した後に動画パーツを非表示にしたり、 動画終了時に End Screen を表示できるようになれば、より高機 能な動画が実現できると思います まとめ Jetpack Compose ライフを楽しみましょう!