Personalizaciones de la IU

Media3 proporciona un PlayerView predeterminado que ofrece algunas opciones de personalización. Para cualquier personalización adicional, 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, un usuario debe 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 ú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));

Escucha 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 información.

La actualización de la IU puede ser costosa y, a menudo, llegan varios eventos de jugadores juntos. Para evitar actualizar la IU con demasiada frecuencia en un período breve, por lo 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.
}