Media3 udostępnia domyślny element PlayerView, który oferuje pewne opcje dostosowywania.
Zastąp elementy rysowalne
PlayerView używa PlayerControlView do wyświetlania elementów sterujących odtwarzaniem i paska postępu. Elementy rysowalne używane przez PlayerControlView mogą zostać zastąpione przez elementy rysowalne o tych samych nazwach zdefiniowane w aplikacji. Listę elementów rysowalnych, które można zastąpić, znajdziesz w dokumentacji PlayerControlView.
Jeśli deweloperzy aplikacji chcą wprowadzić dalsze zmiany, muszą zaimplementować własne komponenty interfejsu. Na początek możesz zapoznać się z tymi sprawdzonymi metodami.
Sprawdzone metody
Podczas wdrażania interfejsu multimedialnego, który łączy się z Player Media3 (np. ExoPlayer, MediaController lub niestandardową implementacją Player), zalecamy stosowanie tych sprawdzonych metod, aby zapewnić jak najlepsze wrażenia z korzystania z interfejsu.
Przycisk odtwarzania/wstrzymania
Przycisk odtwarzania i wstrzymywania nie odpowiada bezpośrednio jednemu stanowi odtwarzacza. Użytkownik powinien mieć na przykład 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 użytkowe, które pozwalają zdecydować, który przycisk wyświetlić (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));
Słuchanie aktualizacji stanu
Komponent interfejsu musi dodać Player.Listener, aby otrzymywać informacje o zmianach stanu, które wymagają odpowiedniej aktualizacji interfejsu. Więcej informacji znajdziesz w sekcji Nasłuchiwanie zdarzeń odtwarzania.
Odświeżanie interfejsu może być kosztowne, a często występuje wiele zdarzeń odtwarzacza jednocześnie. Aby uniknąć zbyt częstego odświeżania interfejsu w krótkim czasie, lepiej jest nasłuchiwać tylko zdarzenia onEvents i na jego podstawie 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ń
Komponent interfejsu 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(COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT));
Migawka pierwszej klatki i wyświetlanie obrazu
Gdy komponent interfejsu wyświetla film lub obrazy, zwykle używa zastępczego widoku migawki, dopóki nie będzie dostępna pierwsza klatka lub obraz. Dodatkowo odtwarzanie mieszanych treści wideo i obrazów wymaga ukrywania i wyświetlania widoku obrazu w odpowiednich momentach.
Typowym sposobem obsługi tych aktualizacji jest nasłuchiwanie Player.Listener.onEvents() pod kątem zmian w wybranych ścieżkach (EVENT_TRACKS_CHANGED) i wyrenderowania pierwszej klatki wideo (EVENT_RENDERED_FIRST_FRAME), a także ImageOutput.onImageAvailable() pod kątem 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, 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. }