Media3 fornisce un PlayerView
predefinito che offre alcune opzioni di personalizzazione.
Override delle risorse disegnabili
PlayerView
utilizza PlayerControlView
per visualizzare i controlli di riproduzione e la barra di avanzamento. Le risorse grafiche utilizzate da PlayerControlView
possono essere sostituite da
risorse grafiche con gli stessi nomi definiti nella tua applicazione. Consulta la documentazione
PlayerControlView
per un elenco di risorse disegnabili di controllo che possono
essere sostituite.
Per qualsiasi ulteriore personalizzazione, gli sviluppatori di app sono tenuti a implementare i propri componenti UI. Tuttavia, ecco alcune best practice che possono aiutarti a iniziare.
Best practice
Quando implementi un'interfaccia utente multimediale che si connette a Media3 Player
(ad esempio
ExoPlayer
, MediaController
o un'implementazione Player
personalizzata), ti consigliamo di seguire queste best practice per un'esperienza utente ottimale.
Pulsante Riproduci/Pausa
Il pulsante di riproduzione e pausa non corrisponde direttamente a un singolo stato del player. Ad esempio, un utente dovrebbe essere in grado di riavviare la riproduzione dopo che è terminata o non è riuscita, anche se il player non è in pausa.
Per semplificare l'implementazione, Media3 fornisce metodi di utilità 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 sullo stato
Il componente UI deve aggiungere un Player.Listener
per essere informato delle modifiche
di stato che richiedono un aggiornamento dell'interfaccia utente corrispondente. Per informazioni dettagliate, consulta Ascoltare gli eventi di riproduzione.
L'aggiornamento della UI può essere costoso e spesso arrivano più eventi del player
contemporaneamente. Per evitare di aggiornare troppo spesso la UI in un breve periodo di tempo, in genere è meglio ascoltare solo onEvents
e attivare gli aggiornamenti della UI 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 UI generico che potrebbe dover funzionare con diverse implementazioni 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 frame e visualizzazione dell'immagine
Quando un componente UI mostra video o immagini, in genere utilizza una visualizzazione placeholder fino a quando non è disponibile il primo frame o la prima immagine reali. Inoltre, la riproduzione mista di video e immagini richiede di nascondere e mostrare la visualizzazione delle immagini nei momenti opportuni.
Un pattern comune per gestire questi aggiornamenti è ascoltare
Player.Listener.onEvents()
per qualsiasi modifica alle tracce selezionate
(EVENT_TRACKS_CHANGED
) e per quando è stato eseguito il rendering del primo fotogramma del 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. }