Personalizaciones de la IU

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.
}