Media3 bietet eine Standard-PlayerView
mit einigen Anpassungsoptionen.
Drawables überschreiben
PlayerView
verwendet PlayerControlView
, um die Wiedergabesteuerung und die Fortschrittsanzeige darzustellen. Die von PlayerControlView
verwendeten Drawables können durch Drawables mit denselben Namen überschrieben werden, die in Ihrer Anwendung definiert sind. Eine Liste der Steuerelement-Drawables, die überschrieben werden können, finden Sie in der Dokumentation zu PlayerControlView
.
Für weitere Anpassungen müssen App-Entwickler eigene UI-Komponenten implementieren. Hier sind einige Best Practices, die Ihnen den Einstieg erleichtern können.
Best Practices
Bei der Implementierung einer Medien-UI, die mit einem Media3-Player
verbunden ist (z. B. ExoPlayer
, MediaController
oder eine benutzerdefinierte Player
-Implementierung), sollten Apps diese Best Practices für eine optimale UI-Erfahrung befolgen.
Schaltfläche „Wiedergabe/Pause“
Die Schaltfläche „Wiedergabe und Pause“ entspricht nicht direkt einem einzelnen Playerstatus. Ein Nutzer sollte beispielsweise die Wiedergabe neu starten können, nachdem sie beendet wurde oder fehlgeschlagen ist, auch wenn der Player nicht pausiert ist.
Um die Implementierung zu vereinfachen, bietet Media3 Hilfsmethoden, mit denen Sie entscheiden können, welche Schaltfläche angezeigt werden soll (Util.shouldShowPlayButton
), und Schaltflächenklicks verarbeiten können (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 anhören
Die UI-Komponente muss ein Player.Listener
hinzufügen, um über Zustandsänderungen informiert zu werden, die ein entsprechendes UI-Update erfordern. Weitere Informationen finden Sie unter Wiedergabeereignisse abhören.
Das Aktualisieren der Benutzeroberfläche kann kostspielig sein und mehrere Player-Ereignisse kommen oft gleichzeitig an. Um zu vermeiden, dass die Benutzeroberfläche in kurzer Zeit zu oft aktualisiert wird, ist es in der Regel besser, nur auf onEvents
zu warten und UI-Updates von dort aus 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 universelle UI-Komponente, die möglicherweise mit verschiedenen Player
-Implementierungen verwendet wird, sollte die verfügbaren Player-Befehle prüfen, um Schaltflächen ein- oder auszublenden und das Aufrufen nicht unterstützter Methoden zu vermeiden:
Kotlin
nextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));
Erster Frame-Verschluss und Bildanzeige
Wenn in einer UI-Komponente Videos oder Bilder angezeigt werden, wird in der Regel ein Platzhalter-Shutter-View verwendet, bis der erste Frame oder das erste Bild verfügbar ist. Außerdem muss bei der gemischten Wiedergabe von Videos und Bildern die Bildansicht zu geeigneten Zeiten ein- und ausgeblendet werden.
Ein gängiges Muster für die Verarbeitung dieser Aktualisierungen besteht darin, auf Player.Listener.onEvents()
zu warten, wenn sich die ausgewählten Tracks (EVENT_TRACKS_CHANGED
) ändern und wenn der erste Videoframes gerendert wurde (EVENT_RENDERED_FIRST_FRAME
), sowie auf ImageOutput.onImageAvailable()
, wenn ein neues Bild verfügbar ist:
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. }