Anpassungen der Benutzeroberfläche

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