Dostosowywanie interfejsu

Media3 udostępnia domyślny PlayerView, który 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 (na przykład ExoPlayer, MediaController lub niestandardowa implementacja Player), zalecamy stosowanie tych sprawdzonych metod, aby zapewnić jak najlepsze wrażenia z interfejsu.

Przycisk odtwarzania/wstrzymania

Przyciski odtwarzania i pauzy nie odpowiadają bezpośrednio stanowi pojedynczego odtwarzacza. Na przykład użytkownik powinien mieć możliwość ponownego uruchomienia odtwarzania po jego zakończeniu lub błędzie, nawet jeśli odtwarzacz nie jest wstrzymany.

Aby uprościć implementację, Media3 udostępnia przydatne metody, 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 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 słuchać tylko polecenia onEvents i tam uruchamiać 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ń

Komponent UI ogólnego przeznaczenia, który może wymagać współpracy z różnymi implementacjami 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. Dodatkowo odtwarzanie obrazu i filmu 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.
}