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