UI 맞춤설정

Media3는 일부 맞춤설정 옵션을 제공하는 기본 PlayerView를 제공합니다.

드로어블 재정의

PlayerViewPlayerControlView를 사용하여 재생 컨트롤과 진행률 표시줄을 표시합니다. PlayerControlView에서 사용하는 드로어블은 애플리케이션에 정의된 이름이 같은 드로어블로 재정의할 수 있습니다. 재정의할 수 있는 제어 드로어블 목록은 PlayerControlView 문서를 참고하세요.

추가 맞춤설정의 경우 앱 개발자는 자체 UI 구성요소를 구현해야 합니다. 다음은 시작하는 데 도움이 되는 몇 가지 권장사항입니다.

권장사항

Media3 Player (예: ExoPlayer, MediaController 또는 맞춤 Player 구현)에 연결되는 미디어 UI를 구현할 때 앱은 최상의 UI 환경을 위해 다음 권장사항을 따라야 합니다.

재생/일시중지 버튼

재생 및 일시중지 버튼은 단일 플레이어 상태에 직접 대응하지 않습니다. 예를 들어 플레이어가 일시중지되지 않은 경우에도 사용자는 재생이 종료되거나 실패한 후 재생을 다시 시작할 수 있어야 합니다.

구현을 간소화하기 위해 Media3에서는 표시할 버튼 (Util.shouldShowPlayButton)을 결정하고 버튼 누름(Util.handlePlayPauseButtonAction)을 처리하는 유틸리티 메서드를 제공합니다.

Kotlin

val shouldShowPlayButton: Boolean = Util.shouldShowPlayButton(player)
playPauseButton.setImageDrawable(if (shouldShowPlayButton) playDrawable else pauseDrawable)
playPauseButton.setOnClickListener { Util.handlePlayPauseButtonAction(player) }

자바

boolean shouldShowPlayButton = Util.shouldShowPlayButton(player);
playPauseButton.setImageDrawable(shouldShowPlayButton ? playDrawable : pauseDrawable);
playPauseButton.setOnClickListener(view -> Util.handlePlayPauseButtonAction(player));

상태 업데이트 수신

UI 구성요소는 해당 UI 업데이트가 필요한 상태 변경을 알리기 위해 Player.Listener를 추가해야 합니다. 자세한 내용은 재생 이벤트 수신을 참고하세요.

UI를 새로고침하는 데는 비용이 많이 들 수 있으며 여러 플레이어 이벤트가 한 번에 도착하는 경우가 많습니다. 짧은 시간 내에 UI가 너무 자주 새로고침되지 않도록 하려면 일반적으로 onEvents만 수신하고 여기에서 UI 업데이트를 트리거하는 것이 좋습니다.

Kotlin

player.addListener(object : Player.Listener{
  override fun onEvents(player: Player, events: Player.Events){
    if (events.containsAny(
        Player.EVENT_PLAY_WHEN_READY_CHANGED,
        Player.EVENT_PLAYBACK_STATE_CHANGED,
        Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED)) {
      updatePlayPauseButton()
    }
    if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) {
      updateRepeatModeButton()
    }
  }
})

자바

player.addListener(new Player.Listener() {
  @Override
  public void onEvents(Player player, Player.Events events) {
    if (events.containsAny(
        Player.EVENT_PLAY_WHEN_READY_CHANGED,
        Player.EVENT_PLAYBACK_STATE_CHANGED,
        Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED)) {
      updatePlayPauseButton();
    }
    if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) {
      updateRepeatModeButton();
    }
  }
});

사용 가능한 명령어 처리

다양한 Player 구현과 함께 작동해야 할 수 있는 범용 UI 구성요소는 사용 가능한 플레이어 명령어를 확인하여 버튼을 표시하거나 숨기고 지원되지 않는 메서드를 호출하지 않아야 합니다.

Kotlin

nextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)

자바

nextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));

첫 번째 프레임 셔터 및 이미지 표시

UI 구성요소는 동영상이나 이미지를 표시할 때 실제 첫 번째 프레임이나 이미지가 제공될 때까지 자리표시자 셔터 뷰를 사용합니다. 또한 동영상과 이미지를 혼합하여 재생하려면 적절한 시간에 이미지 뷰를 숨기고 표시해야 합니다.

이러한 업데이트를 처리하는 일반적인 패턴은 선택한 트랙(EVENT_TRACKS_CHANGED)의 변경사항과 첫 번째 동영상 프레임이 렌더링된 시점(EVENT_RENDERED_FIRST_FRAME), 새 이미지가 제공되는 시점의 ImageOutput.onImageAvailable()Player.Listener.onEvents()으로 수신하는 것입니다.

Kotlin

override fun onEvents(player: Player, events: Player.Events) {
  if (events.contains(Player.EVENT_TRACKS_CHANGED)) {
    // If no video or image track: show shutter, hide image view.
    // Otherwise: do nothing to wait for first frame or image.
  }
  if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) {
    // Hide shutter, hide image view.
  }
}

override fun onImageAvailable(presentationTimeUs: Long, bitmap: Bitmap) {
  // Show shutter, set image and show image view.
}

Java

@Override
public void onEvents(Player player, Events events) {
  if (events.contains(Player.EVENT_TRACKS_CHANGED)) {
    // If no video or image track: show shutter, hide image view.
    // Otherwise: do nothing to wait for first frame or image.
  }
  if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) {
    // Hide shutter, hide image view.
  }
}

@Override
public void onImageAvailable(long presentationTimeUs, Bitmap bitmap) {
  // Show shutter, set image and show image view.
}