Utilizzare i controlli per il trasporto

Crea app migliori con Compose
Crea UI accattivanti con codice minimo utilizzando Jetpack Compose per Android TV OS.

Il toolkit dell'interfaccia utente Leanback dispone di controlli di riproduzione che offrono un'esperienza utente migliorata. Per le app video, i controlli di trasporto supportano lo scrubbing video con i controlli di avanzamento e riavvolgimento. Durante lo scorrimento, il display mostra le miniature per aiutarti a navigare nel video.

La libreria include classi astratte e implementazioni predefinite e pronte all'uso che offrono un controllo più granulare per gli sviluppatori. Utilizzando le implementazioni predefinite, puoi creare rapidamente un'app ricca di funzionalità senza scrivere molto codice. Se hai bisogno di una maggiore personalizzazione, puoi estendere uno qualsiasi dei componenti predefiniti della libreria.

Controlli e player

Il toolkit dell'interfaccia utente Leanback separa l'interfaccia utente dei controlli di trasporto dal player che riproduce il video. Ciò si ottiene con due componenti: un frammento di supporto della riproduzione per visualizzare i controlli di trasporto (e facoltativamente il video) e un adattatore del player per incapsulare un media player.

Frammento di riproduzione

L'attività UI della tua app deve utilizzare un PlaybackSupportFragment o un VideoSupportFragment. Entrambi contengono i controlli di trasporto leanback:

Puoi personalizzare il ObjectAdapter di un frammento per migliorare la UI. Ad esempio, utilizza setAdapter() per aggiungere una riga di "video correlati".

PlayerAdapter

PlayerAdapter è una classe astratta che controlla il lettore multimediale sottostante. Gli sviluppatori possono scegliere l'implementazione MediaPlayerAdapter predefinita o scrivere la propria implementazione di questa classe.

Unire i pezzi

Devi utilizzare una sorta di "colla di controllo" per collegare il frammento di riproduzione al player. La libreria leanback fornisce due tipi di colla:

leanback transport control glue

Se vuoi che la tua app supporti lo scrubbing video, devi utilizzare PlaybackTransportControlGlue.

Devi anche specificare un "host di collegamento" che collega il glue al frammento di riproduzione, disegna i controlli di trasporto nella UI e ne mantiene lo stato e trasferisce gli eventi di controllo del trasporto al glue. L'host deve corrispondere al tipo di frammento di riproduzione. Utilizza PlaybackSupportFragmentGlueHost con un PlaybackFragment e VideoSupportFragmentGlueHost con un VideoFragment.

Ecco un'illustrazione che mostra come si combinano i vari elementi di un controllo di trasporto leanback:

leanback transport control glue

Il codice che unisce la tua app deve trovarsi all'interno di PlaybackSupportFragment o VideoSupportFragment che definisce la UI.

Nell'esempio seguente, l'app crea un'istanza diPlaybackTransportControlGlue, la denomina playerGlue e connette il relativo VideoSupportFragment a un MediaPlayerAdapter appena creato. Poiché si tratta di un VideoSupportFragment il codice di configurazione chiama setHost() per allegare un VideoSupportFragmentGlueHost a playerGlue. Il codice è incluso nella classe che estende VideoSupportFragment.

Kotlin

class MyVideoFragment : VideoSupportFragment() {

  fun onCreate(savedInstanceState: Bundle) {
      super.onCreate(savedInstanceState)
      val playerGlue = PlaybackTransportControlGlue(getActivity(),
          MediaPlayerAdapter(getActivity()))
      playerGlue.setHost(VideoSupportFragmentGlueHost(this))
      playerGlue.addPlayerCallback(object : PlaybackGlue.PlayerCallback() {
          override fun onPreparedStateChanged(glue: PlaybackGlue) {
              if (glue.isPrepared()) {
                  playerGlue.seekProvider = MySeekProvider()
                  playerGlue.play()
              }
          }
      })
      playerGlue.setSubtitle("Leanback artist")
      playerGlue.setTitle("Leanback team at work")
      val uriPath = "android.resource://com.example.android.leanback/raw/video"
      playerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath))
  }
}

Java

public class MyVideoFragment extends VideoSupportFragment {

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      final PlaybackTransportControlGlue<MediaPlayerAdapter> playerGlue =
              new PlaybackTransportControlGlue(getActivity(),
                      new MediaPlayerAdapter(getActivity()));
      playerGlue.setHost(new VideoSupportFragmentGlueHost(this));
      playerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
          @Override
          public void onPreparedStateChanged(PlaybackGlue glue) {
              if (glue.isPrepared()) {
                  playerGlue.setSeekProvider(new MySeekProvider());
                  playerGlue.play();
              }
          }
      });
      playerGlue.setSubtitle("Leanback artist");
      playerGlue.setTitle("Leanback team at work");
      String uriPath = "android.resource://com.example.android.leanback/raw/video";
      playerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath));
  }
}

Tieni presente che il codice di configurazione definisce anche un PlayerAdapter.Callback per gestire gli eventi del lettore multimediale.

Personalizzazione del glue dell'interfaccia utente

Puoi personalizzare PlaybackBannerControlGlue e PlaybackTransportControlGlue per modificare PlaybackControlsRow.

Personalizzare il titolo e la descrizione

Per personalizzare il titolo e la descrizione nella parte superiore dei controlli di riproduzione, esegui l'override di onCreateRowPresenter():

Kotlin

override fun onCreateRowPresenter(): PlaybackRowPresenter {
    return super.onCreateRowPresenter().apply {
        (this as? PlaybackTransportRowPresenter)
                ?.setDescriptionPresenter(MyCustomDescriptionPresenter())
    }
}

Java

@Override
protected PlaybackRowPresenter onCreateRowPresenter() {
  PlaybackTransportRowPresenter presenter = (PlaybackTransportRowPresenter) super.onCreateRowPresenter();
  presenter.setDescriptionPresenter(new MyCustomDescriptionPresenter());
  return presenter;
}

Aggiunta di controlli

Il controllo glue mostra i controlli per le azioni in un PlaybackControlsRow.

Le azioni in PlaybackControlsRow sono assegnate a due gruppi: azioni principali e azioni secondarie. I controlli per il gruppo principale vengono visualizzati sopra la barra di ricerca e quelli per il gruppo secondario sotto la barra di ricerca. Inizialmente, è presente una sola azione principale per il pulsante di riproduzione/pausa e nessuna azione secondaria.

Puoi aggiungere azioni ai gruppi principali e secondari sostituendo onCreatePrimaryActions() e onCreateSecondaryActions().

Kotlin

private lateinit var repeatAction: PlaybackControlsRow.RepeatAction
private lateinit var pipAction: PlaybackControlsRow.PictureInPictureAction
private lateinit var thumbsUpAction: PlaybackControlsRow.ThumbsUpAction
private lateinit var thumbsDownAction: PlaybackControlsRow.ThumbsDownAction
private lateinit var skipPreviousAction: PlaybackControlsRow.SkipPreviousAction
private lateinit var skipNextAction: PlaybackControlsRow.SkipNextAction
private lateinit var fastForwardAction: PlaybackControlsRow.FastForwardAction
private lateinit var rewindAction: PlaybackControlsRow.RewindAction

override fun onCreatePrimaryActions(primaryActionsAdapter: ArrayObjectAdapter) {
    // Order matters, super.onCreatePrimaryActions() will create the play / pause action.
    // Will display as follows:
    // play/pause, previous, rewind, fast forward, next
    //   > /||      |<        <<        >>         >|
    super.onCreatePrimaryActions(primaryActionsAdapter)
    primaryActionsAdapter.apply {
        add(skipPreviousAction)
        add(rewindAction)
        add(fastForwardAction)
        add(skipNextAction)
    }
}

override fun onCreateSecondaryActions(adapter: ArrayObjectAdapter?) {
    super.onCreateSecondaryActions(adapter)
    adapter?.apply {
        add(thumbsDownAction)
        add(thumbsUpAction)
    }
}

Java

private PlaybackControlsRow.RepeatAction repeatAction;
private PlaybackControlsRow.PictureInPictureAction pipAction;
private PlaybackControlsRow.ThumbsUpAction thumbsUpAction;
private PlaybackControlsRow.ThumbsDownAction thumbsDownAction;
private PlaybackControlsRow.SkipPreviousAction skipPreviousAction;
private PlaybackControlsRow.SkipNextAction skipNextAction;
private PlaybackControlsRow.FastForwardAction fastForwardAction;
private PlaybackControlsRow.RewindAction rewindAction;

@Override
protected void onCreatePrimaryActions(ArrayObjectAdapter primaryActionsAdapter) {
    // Order matters, super.onCreatePrimaryActions() will create the play / pause action.
    // Will display as follows:
    // play/pause, previous, rewind, fast forward, next
    //   > /||      |<        <<        >>         >|
    super.onCreatePrimaryActions(primaryActionsAdapter);
    primaryActionsAdapter.add(skipPreviousAction);
    primaryActionsAdapter.add(rewindAction);
    primaryActionsAdapter.add(fastForwardAction);
    primaryActionsAdapter.add(skipNextAction);
}

@Override
protected void onCreateSecondaryActions(ArrayObjectAdapter adapter) {
    super.onCreateSecondaryActions(adapter);
    adapter.add(thumbsDownAction);
    adapter.add(thumbsUpAction);
}

Devi eseguire l'override di onActionClicked() per gestire le nuove azioni.

Kotlin

override fun onActionClicked(action: Action) {
    when(action) {
        rewindAction -> {
            // Handle Rewind
        }
        fastForwardAction -> {
            // Handle FastForward
        }
        thumbsDownAction -> {
            // Handle ThumbsDown
        }
        thumbsUpAction -> {
            // Handle ThumbsUp
        }
        else ->
            // The superclass handles play/pause and delegates next/previous actions to abstract methods,
            // so those two methods should be overridden rather than handling the actions here.
            super.onActionClicked(action)
    }
}

override fun next() {
    // Skip to next item in playlist.
}

override fun previous() {
    // Skip to previous item in playlist.
}

Java

@Override
public void onActionClicked(Action action) {
    if (action == rewindAction) {
        // Handle Rewind
    } else if (action == fastForwardAction ) {
        // Handle FastForward
    } else if (action == thumbsDownAction) {
        // Handle ThumbsDown
    } else if (action == thumbsUpAction) {
        // Handle ThumbsUp
    } else {
        // The superclass handles play/pause and delegates next/previous actions to abstract methods,
        // so those two methods should be overridden rather than handling the actions here.
        super.onActionClicked(action);
    }
}

@Override
public void next() {
    // Skip to next item in playlist.
}

@Override
public void previous() {
    // Skip to previous item in playlist.
}

In casi speciali, potresti voler implementare un PlaybackTransportRowPresenter per eseguire il rendering di controlli personalizzati e rispondere alle azioni di ricerca utilizzando PlaybackSeekUi.

Scorrimento del video

Se la tua app utilizza un VideoSupportFragment e vuoi supportare lo scrubbing dei video.

strusciarsi

Devi fornire un'implementazione di PlaybackSeekDataProvider. Questo componente fornisce le miniature dei video utilizzate durante lo scorrimento. Devi implementare il tuo provider estendendo PlaybackSeekDataProvider. Vedi l'esempio nell' app Leanback Showcase. .