Dostosowywanie interfejsu

Media3 udostępnia domyślny PlayerView, który zawiera kilka opcji dostosowywania.

Zastępowanie drawable

PlayerView używa PlayerControlView do wyświetlania elementów sterujących odtwarzaniem i paska postępu. Obiekty graficzne używane przez PlayerControlView mogą zostać zastąpione przez obiekty graficzne o tych samych nazwach zdefiniowanych w aplikacji. Aby poznać listę obiektów rysowanych, które można zastąpić, zapoznaj się z dokumentacją PlayerControlView.

W przypadku dalszych dostosowań deweloperzy aplikacji powinni zaimplementować własne komponenty interfejsu. Oto kilka sprawdzonych metod, które pomogą Ci zacząć.

Sprawdzone metody

Podczas implementowania interfejsu użytkownika multimediów, który łączy się z Media3 Player (np. ExoPlayer, MediaController lub niestandardowa implementacja Player), zalecamy stosowanie tych sprawdzonych metod w celu zapewnienia najlepszego interfejsu.

Przycisk Odtwórz/Wstrzymaj

Przycisk odtwarzania i wstrzymywania nie odpowiada bezpośrednio stanowi pojedynczego odtwarzacza. Użytkownik powinien na przykład mieć możliwość ponownego uruchomienia odtwarzania po jego zakończeniu lub niepowodzeniu, nawet jeśli odtwarzacz nie jest wstrzymany.

Aby uprościć implementację, Media3 udostępnia metody pomocnicze, które umożliwiają określenie, który przycisk ma być wyświetlany (Util.shouldShowPlayButton), oraz obsługę naciśnięcia przycisku (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));

Słuchanie informacji o stanach

Komponent interfejsu musi dodać Player.Listener, aby otrzymywać informacje o zmianach stanu, które wymagają odpowiedniej aktualizacji interfejsu. Więcej informacji znajdziesz w sekcji Odtwarzanie zdarzeń.

Odświeżanie interfejsu może być kosztowne, a wiele zdarzeń dotyczących graczy często dociera do serwera w tym samym czasie. Aby uniknąć zbyt częstego odświeżania interfejsu w krótkim czasie, lepiej jest słuchać tylko onEvents i w ten sposób wywoływać aktualizacje interfejsu:

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 użytkownika, który może być używany w różnych implementacjach 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(Player.COMMAND_SEEK_TO_NEXT)

Java

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

Zasłona pierwszego klatki i wyświetlanie obrazu

Gdy komponent interfejsu wyświetla film lub obrazy, zwykle używa widoku zasłony zastępczej, dopóki nie będzie dostępne pierwsze prawidłowe zdjęcie lub pierwsza prawidłowa klatka. Ponadto odtwarzanie mieszanki filmu i obrazu wymaga ukrywania i wyświetlania obrazu w odpowiednich momentach.

Typowym sposobem obsługi tych aktualizacji jest słuchanie zdarzenia Player.Listener.onEvents() w przypadku każdej zmiany w wybranych ścieżkach (EVENT_TRACKS_CHANGED) i w momencie wyrenderowania pierwszego kadru filmu (EVENT_RENDERED_FIRST_FRAME), a także zdarzenia ImageOutput.onImageAvailable() w przypadku dostępności 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, 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.
}