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 diese 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 util-Methoden, mit denen entschieden wird, welche Schaltfläche angezeigt werden soll (Util.shouldShowPlayButton) und das Drücken von Schaltflächen (Util.handlePlayPauseButtonAction) zu verarbeiten:

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

Statusaktualisierungen überwachen

Die UI-Komponente muss eine Player.Listener hinzufügen, um über Statusä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 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));

Auslöser für ersten Frame 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 richtigen 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.
}