Media3 udostępnia domyślny PlayerView
, który oferuje pewne opcje dostosowywania.
Zastępowanie zasobów rysowalnych
PlayerView
używa PlayerControlView
do wyświetlania elementów sterujących odtwarzaniem i paska postępu. Elementy rysowalne używane przez PlayerControlView
można zastąpić elementami rysowalnymi o tych samych nazwach zdefiniowanymi w aplikacji. Listę elementów rysowalnych sterowania, które można zastąpić, znajdziesz w dokumentacji PlayerControlView
.
W przypadku dalszego dostosowywania deweloperzy aplikacji powinni wdrażać własne komponenty interfejsu. Oto jednak kilka sprawdzonych metod, które pomogą Ci zacząć.
Sprawdzone metody
Podczas wdrażania interfejsu multimediów, który łączy się z biblioteką Media3 Player
(np. ExoPlayer
, MediaController
lub niestandardową implementacją Player
), aplikacje powinny stosować te sprawdzone metody, aby zapewnić jak najlepsze wrażenia użytkownikom.
Przycisk odtwarzania/wstrzymywania
Przycisk odtwarzania i wstrzymywania nie odpowiada bezpośrednio jednemu stanowi 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 narzędziowe, które pozwalają określić, który przycisk ma się wyświetlać (Util.shouldShowPlayButton
), i 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ć Player.Listener
, aby otrzymywać informacje o zmianach stanu, które wymagają odpowiedniej aktualizacji interfejsu. Więcej informacji znajdziesz w artykule Słuchanie 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 onEvents
i stamtąd 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, który może wymagać współpracy z różnymi Player
implementacjami, 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));
Migawka pierwszej 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ępna pierwsza klatka lub obraz. Dodatkowo odtwarzanie mieszane filmów i obrazów 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 zmiany wybranych ścieżek (EVENT_TRACKS_CHANGED
) i wyrenderowania 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, 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. }