Personalizaciones de la IU

Media3 proporciona un PlayerView predeterminado que ofrece algunas opciones de personalización.

Anula los 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 que tengan los mismos nombres definidos en tu aplicación. Consulta la documentación de PlayerControlView para obtener una lista de elementos de diseño de control 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 se implementa una IU de contenido 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 solo estado del reproductor. Por ejemplo, un usuario debería poder reiniciar la reproducción después de que finalizó o falló, incluso si el reproductor no está pausado.

Para simplificar la implementación, Media3 proporciona métodos de utilidad para decidir qué botón mostrar (Util.shouldShowPlayButton) y controlar las presiones de los 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 las 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 detalles.

Actualizar la IU puede ser costoso, y suelen llegar varios eventos del reproductor juntos. Para evitar actualizar la IU con demasiada frecuencia en un período breve, suele ser 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 deba funcionar con diferentes implementaciones de Player debe verificar los comandos disponibles del reproductor para mostrar u ocultar botones y evitar llamar a métodos no admitidos:

Kotlin

nextButton.isEnabled = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT)

Java

nextButton.setEnabled(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT));

Obturador del primer fotograma y visualización de la imagen

Cuando un componente de la IU muestra un video 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 reales. Además, la reproducción mixta de video e imagen 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 (EVENT_RENDERED_FIRST_FRAME), así como ImageOutput.onImageAvailable() para cuando hay 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, 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.
}