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