Media3 proporciona un PlayerView
predeterminado que incluye algunas opciones de personalización. Para una mayor personalización, se espera que los desarrolladores de apps implementen sus propios componentes de IU.
Prácticas recomendadas
Cuando se implementa una IU multimedia que se conecta 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 Reproducir/Pausar
El botón de reproducción y pausa no corresponde directamente a un estado de un solo jugador. Por ejemplo, el usuario debería poder reiniciar la reproducción después de que haya finalizado o haya fallado, incluso si el reproductor no está pausado.
Para simplificar la implementación, Media3 proporciona métodos útiles para decidir qué botón mostrar (Util.shouldShowPlayButton
) y controlar las presiones 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 escuchar actualizaciones de estado
El componente de la IU debe agregar un Player.Listener
para recibir información sobre 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.
La actualización de la IU puede ser costosa y, a menudo, los eventos de varios jugadores llegan juntos. Para evitar actualizar la IU con demasiada frecuencia en un período breve, en general, 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 pueda necesitar trabajar con diferentes implementaciones de Player
debe verificar los comandos del reproductor disponibles para mostrar o ocultar botones y evitar llamar a métodos no compatibles:
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 la imagen
Cuando un componente de la IU muestra imágenes o videos, suele usar una vista del obturador del marcador de posición hasta que el primer fotograma o la primera imagen reales estén disponibles. Además, la reproducción de imágenes y videos mezclados 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 renderiza el primer fotograma de video (EVENT_RENDERED_FIRST_FRAME
), así como ImageOutput.onImageAvailable
para cuando haya una imagen nueva 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. }