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