Dostosowywanie interfejsu

Media3 udostępnia domyślną PlayerView, która zawiera opcje dostosowywania. W przypadku dalszych dostosowań deweloperzy aplikacji powinni implementować własne komponenty interfejsu.

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

Przyciski odtwarzania i pauzy nie odpowiadają bezpośrednio stanowi pojedynczego odtwarzacza. Użytkownik powinien 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 przydatne metody, które pozwalają określić, który przycisk ma się wyświetlić (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));

Słuchanie informacji o stanie

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

Odświeżanie interfejsu może być kosztowne, a wiele zdarzeń dotyczących odtwarzacza często dociera do nas jednocześnie. 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 zastępczego, 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 we właściwych momentach.

Typowym sposobem obsługi tych aktualizacji jest słuchanie 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 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.
}