Personnalisations de l'UI

Media3 fournit un PlayerView par défaut qui propose des options de personnalisation. Pour toute personnalisation supplémentaire, les développeurs d'applications doivent implémenter leurs propres composants d'interface utilisateur.

Bonnes pratiques

Lorsque vous implémentez une UI multimédia qui se connecte à un Player Media3 (par exemple, ExoPlayer, MediaController ou une implémentation Player personnalisée), nous vous recommandons de suivre ces bonnes pratiques pour une expérience UI optimale.

Bouton Lecture/Pause

Le bouton de lecture et de mise en pause ne correspond pas directement à l'état d'un seul joueur. Par exemple, un utilisateur doit pouvoir redémarrer la lecture une fois qu'elle s'est terminée ou a échoué, même si le lecteur n'est pas mis en pause.

Pour simplifier l'implémentation, Media3 fournit des méthodes utilitaires permettant de choisir le bouton à afficher (Util.shouldShowPlayButton) et de gérer les pressions sur les boutons (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));

Écouter les mises à jour d'état

Le composant d'UI doit ajouter un Player.Listener pour être informé des modifications d'état qui nécessitent une mise à jour de l'UI correspondante. Pour en savoir plus, consultez la section Écouter les événements de lecture.

L'actualisation de l'interface utilisateur peut être coûteuse, et plusieurs événements de joueur arrivent souvent en même temps. Pour éviter d'actualiser l'UI trop souvent sur une courte période, il est généralement préférable d'écouter uniquement onEvents et de déclencher les mises à jour de l'UI à partir de là:

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();
    }
  }
});

Gérer les commandes disponibles

Un composant d'interface utilisateur à usage général qui peut être compatible avec différentes implémentations Player doit vérifier les commandes de lecteur disponibles pour afficher ou masquer les boutons et éviter d'appeler des méthodes non compatibles:

Kotlin

nextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)

Java

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

Obturateur du premier frame et affichage de l'image

Lorsqu'un composant d'interface utilisateur affiche une vidéo ou des images, il utilise généralement une vue d'obturateur d'espace réservé jusqu'à ce que le premier frame ou l'image réelle soit disponible. De plus, la lecture mixte de vidéos et d'images nécessite de masquer et d'afficher la vue d'image aux moments opportuns.

Un modèle courant pour gérer ces mises à jour consiste à écouter Player.Listener.onEvents pour détecter tout changement dans les pistes sélectionnées (EVENT_TRACKS_CHANGED) et le moment où le premier frame vidéo a été affiché (EVENT_RENDERED_FIRST_FRAME), ainsi que ImageOutput.onImageAvailable pour savoir quand une nouvelle image est 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.
}