Media3 fornisce un PlayerView
predefinito che offre alcune opzioni di personalizzazione.
Sostituire gli elementi drawable
PlayerView
utilizza PlayerControlView
per visualizzare i controlli di riproduzione e la barra di avanzamento. Gli elementi drawable utilizzati da PlayerControlView
possono essere sostituiti da elementi drawable con gli stessi nomi definiti nell'applicazione. Consulta la documentazione di PlayerControlView
per un elenco di elementi grafici di controllo che possono essere sostituiti.
Per qualsiasi ulteriore personalizzazione, gli sviluppatori di app devono implementare i propri componenti dell'interfaccia utente. Tuttavia, ecco alcune best practice che possono aiutarti a iniziare.
Best practice
Quando implementi un'interfaccia utente multimediale che si connette a un Player
Media3 (ad esempio ExoPlayer
, MediaController
o un'implementazione Player
personalizzata), ti consigliamo di seguire queste best practice per un'esperienza utente ottimale.
Pulsante Riproduci/Metti in pausa
I pulsanti di riproduzione e messa in pausa non corrispondono direttamente allo stato di un singolo player. Ad esempio, un utente dovrebbe essere in grado di riavviare la riproduzione dopo che è terminata o non è andata a buon fine anche se il player non è in pausa.
Per semplificare l'implementazione, Media3 fornisce metodi utili per decidere quale pulsante mostrare (Util.shouldShowPlayButton
) e per gestire le pressioni dei pulsanti (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));
Ascoltare gli aggiornamenti dello stato
Il componente dell'interfaccia utente deve aggiungere un Player.Listener
per essere informato delle modifiche dello stato che richiedono un aggiornamento dell'interfaccia utente corrispondente. Per informazioni dettagliate, consulta Ascoltare gli eventi di riproduzione.
L'aggiornamento dell'interfaccia utente può essere costoso e spesso si verificano più eventi del player contemporaneamente. Per evitare di aggiornare l'interfaccia utente troppo spesso in un breve periodo di tempo, in genere è meglio ascoltare solo onEvents
e attivare gli aggiornamenti dell'interfaccia utente da lì:
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(); } } });
Gestire i comandi disponibili
Un componente dell'interfaccia utente generico che potrebbe dover funzionare con implementazioni diverse di Player
deve controllare i comandi del player disponibili per mostrare o nascondere i pulsanti ed evitare di chiamare metodi non supportati:
Kotlin
nextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));
Otturatore del primo fotogramma e visualizzazione dell'immagine
Quando un componente dell'interfaccia utente mostra video o immagini, in genere utilizza una visualizzazione della tenda placeholder finché non è disponibile il primo frame o l'immagine reale. Inoltre, la riproduzione di video e immagini miste richiede di nascondere e mostrare la visualizzazione delle immagini nei momenti opportuni.
Un pattern comune per gestire questi aggiornamenti è ascoltare
Player.Listener.onEvents()
per eventuali modifiche alle tracce selezionate
(EVENT_TRACKS_CHANGED
) e per quando è stato visualizzato il primo frame video
(EVENT_RENDERED_FIRST_FRAME
), nonché ImageOutput.onImageAvailable()
per quando è disponibile una nuova immagine:
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. }