Media3 bietet eine Standard-PlayerView
mit einigen Anpassungsoptionen. Für weitere Anpassungen müssen App-Entwickler eigene UI-Komponenten implementieren.
Best Practices
Wenn Sie eine Medien-UI implementieren, die eine Verbindung zu einer Media3-Player
herstellt (z. B. ExoPlayer
, MediaController
oder eine benutzerdefinierte Player
-Implementierung), sollten Sie für eine optimale Benutzeroberfläche die folgenden Best Practices beachten.
Schaltfläche „Wiedergabe/Pause“
Die Wiedergabe- und Pause-Schaltflächen entsprechen nicht direkt dem Status eines einzelnen Spielers. Ein Nutzer sollte beispielsweise die Wiedergabe nach dem Ende oder einem Fehler fortsetzen können, auch wenn der Player nicht pausiert ist.
Zur Vereinfachung der Implementierung bietet Media3 Dienstmethoden, mit denen entschieden werden kann, welche Schaltfläche angezeigt werden soll (Util.shouldShowPlayButton
) und wie Tastendrücke verarbeitet werden (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));
Statusupdates empfangen
Der UI-Komponente muss ein Player.Listener
hinzugefügt werden, damit sie über Statusänderungen informiert wird, die eine entsprechende UI-Aktualisierung erfordern. Weitere Informationen finden Sie unter Wiedergabeereignisse abhören.
Das Aktualisieren der Benutzeroberfläche kann kostspielig sein und oft werden mehrere Spielerereignisse gleichzeitig empfangen. Um zu vermeiden, dass die Benutzeroberfläche in kurzer Zeit zu oft aktualisiert wird, ist es im Allgemeinen besser, nur onEvents
zu hören und von dort aus Updates der Benutzeroberfläche auszulösen:
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(); } } });
Verfügbare Befehle verarbeiten
Eine allgemeine UI-Komponente, die mit verschiedenen Player
-Implementierungen funktionieren muss, sollte die verfügbaren Playerbefehle prüfen, um Schaltflächen ein- oder auszublenden und nicht unterstützte Methoden aufzurufen:
Kotlin
nextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));
Verschluss des ersten Frames und Bildanzeige
Wenn in einer UI-Komponente Videos oder Bilder angezeigt werden, wird in der Regel eine Platzhalter-Shutter-Ansicht verwendet, bis der tatsächliche erste Frame oder das erste Bild verfügbar ist. Bei der gemischten Video- und Bildwiedergabe muss die Bildansicht außerdem zum passenden Zeitpunkt ausgeblendet und eingeblendet werden.
Ein gängiges Muster für die Verarbeitung dieser Aktualisierungen besteht darin, Player.Listener.onEvents
auf Änderungen in ausgewählten Tracks (EVENT_TRACKS_CHANGED
) und auf das Rendern des ersten Videoframes (EVENT_RENDERED_FIRST_FRAME
) sowie ImageOutput.onImageAvailable
auf ein neues Bild zu hören:
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. }