Media3 proporciona un PlayerView
predeterminado que ofrece algunas opciones de personalización.
Anula elementos de diseño
PlayerView
usa PlayerControlView
para mostrar los controles de reproducción y la barra de progreso. Los elementos de diseño que usa PlayerControlView
se pueden anular con elementos de diseño con los mismos nombres definidos en tu aplicación. Consulta la documentación de PlayerControlView
para obtener una lista de elementos de diseño de controles que se pueden anular.
Para cualquier otra personalización, se espera que los desarrolladores de apps implementen sus propios componentes de IU. Sin embargo, estas son algunas prácticas recomendadas que pueden ayudarte a comenzar.
Prácticas recomendadas
Cuando implementes una IU de medios que se conecte a un Player
de Media3 (por ejemplo, ExoPlayer
, MediaController
o una implementación personalizada de Player
), se recomienda que las apps sigan estas prácticas recomendadas para obtener la mejor experiencia de IU.
Botón de reproducir/pausar
El botón de reproducción y pausa no corresponde directamente a un solo estado del reproductor. Por ejemplo, un usuario debería poder reiniciar la reproducción después de que finalice o falle, incluso si el reproductor no está en pausa.
Para simplificar la implementación, Media3 proporciona métodos de utilidad para decidir qué botón mostrar (Util.shouldShowPlayButton
) y controlar las pulsaciones de botones (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));
Cómo detectar actualizaciones de estado
El componente de la IU debe agregar un Player.Listener
para estar informado de los cambios de estado que requieren una actualización de la IU correspondiente. Consulta Cómo escuchar eventos de reproducción para obtener más información.
Actualizar la IU puede ser costoso, y los eventos de varios jugadores suelen llegar juntos. Para evitar actualizar la IU con demasiada frecuencia en un período corto, generalmente es mejor escuchar solo onEvents
y activar las actualizaciones de la IU desde allí:
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(); } } });
Controla los comandos disponibles
Un componente de IU de uso general que puede necesitar trabajar con diferentes implementaciones de Player
debe verificar los comandos del reproductor disponibles para mostrar u ocultar botones y evitar llamar a métodos no admitidos:
Kotlin
nextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));
Obturador del primer fotograma y visualización de imágenes
Cuando un componente de la IU muestra videos o imágenes, suele usar una vista de obturador de marcador de posición hasta que está disponible el primer fotograma o la primera imagen real. Además, la reproducción mixta de videos e imágenes requiere ocultar y mostrar la vista de imagen en los momentos adecuados.
Un patrón común para controlar estas actualizaciones es escuchar Player.Listener.onEvents()
para detectar cualquier cambio en las pistas seleccionadas (EVENT_TRACKS_CHANGED
) y cuando se renderizó el primer fotograma de video (EVENT_RENDERED_FIRST_FRAME
), así como ImageOutput.onImageAvailable()
para cuando haya una nueva imagen disponible:
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. }