Dostosowywanie interfejsu

Media3 udostępnia domyślny element PlayerView, który oferuje pewne opcje dostosowywania.

Zastępowanie elementów rysowalnych

Element PlayerView używa elementu PlayerControlView do wyświetlania elementów sterujących odtwarzaniem i paska postępu. Elementy rysowalne używane przez element PlayerControlView można zastąpić elementami rysowalnymi o tych samych nazwach zdefiniowanymi w aplikacji. Listę elementów rysowalnych, które można zastąpić, znajdziesz w PlayerControlView dokumentacji.

Aby wprowadzić dalsze dostosowania, deweloperzy aplikacji muszą zaimplementować własne komponenty interfejsu. Oto jednak kilka sprawdzonych metod, które mogą Ci pomóc w rozpoczęciu pracy.

Sprawdzone metody

Podczas implementowania interfejsu multimediów, który łączy się z elementem Media3 Player (np. ExoPlayer, MediaController lub niestandardową implementacją elementu Player), zalecamy stosowanie tych sprawdzonych metod, aby zapewnić jak najlepsze wrażenia użytkownika.

Przycisk odtwarzania/wstrzymania

Przycisk odtwarzania i wstrzymywania nie odpowiada bezpośrednio jednemu stanowi odtwarzacza. Użytkownik powinien np. móc wznowić odtwarzanie po jego zakończeniu lub niepowodzeniu, nawet jeśli odtwarzacz nie jest wstrzymany.

Aby uprościć implementację, Media3 udostępnia metody narzędziowe, które pozwalają określić, który przycisk ma być wyświetlany (Util.shouldShowPlayButton), oraz obsługiwać naciśnięcia przycisków (Util.handlePlayPauseButtonAction):

Kotlin

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

Java

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

Nasłuchiwanie zmian stanu

Komponent interfejsu musi dodać element Player.Listener, aby otrzymywać informacje o zmianach stanu, które wymagają odpowiedniej aktualizacji interfejsu. Więcej informacji znajdziesz w artykule Nasłuchiwanie zdarzeń odtwarzania.

Odświeżanie interfejsu może być kosztowne, a wiele zdarzeń odtwarzacza często dociera jednocześnie. Aby uniknąć zbyt częstego odświeżania interfejsu w krótkim czasie, lepiej jest nasłuchiwać tylko zdarzeń onEvents i wywoływać aktualizacje interfejsu z tego miejsca:

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()
      }
    }
  }
)

Java

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();
        }
      }
    });

Obsługa dostępnych poleceń

Ogólny komponent interfejsu, który może współpracować z różnymi implementacjami elementu Player, powinien sprawdzać dostępne polecenia odtwarzacza, aby wyświetlać lub ukrywać przyciski i unikać wywoływania nieobsługiwanych metod:

Kotlin

nextButton.isEnabled = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT)

Java

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

Migawka pierwszej klatki i wyświetlanie obrazu

Gdy komponent interfejsu wyświetla film lub obrazy, zwykle używa widoku migawki zastępczej, dopóki nie będzie dostępna prawdziwa pierwsza klatka lub obraz. Ponadto odtwarzanie mieszane filmu i obrazu wymaga ukrywania i wyświetlania widoku obrazu w odpowiednich momentach.

Typowym sposobem obsługi tych aktualizacji jest nasłuchiwanie zdarzeń Player.Listener.onEvents() w przypadku każdej zmiany wybranych ścieżek (EVENT_TRACKS_CHANGED) oraz w przypadku renderowania pierwszej klatki filmu (EVENT_RENDERED_FIRST_FRAME), a także zdarzeń ImageOutput.onImageAvailable() w przypadku udostępnienia nowego obrazu:

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, 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.
}