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