Usar controles de transporte

Criar da melhor forma com o Compose
Crie interfaces incríveis com o mínimo de código usando o Jetpack Compose para o SO do Android TV.

O kit de ferramentas de interface do usuário do YouTube tem controles de reprodução que oferecem uma interface experiência do usuário. Para apps de vídeo, os controles de transporte são compatíveis com a barra de progressão de vídeo. com os controles para frente e para trás. Ao arrastar o marcador, a tela mostra miniaturas para ajudar a navegar pelo vídeo.

A biblioteca inclui classes abstratas, bem como implementações pré-criadas e prontas para uso. que oferecem controle mais granular para os desenvolvedores. Usando o modelo você pode criar rapidamente um app repleto de recursos sem muita programação. Se precisar de mais personalização, você pode estender qualquer um dos componentes de solução.

Controles e player

O kit de ferramentas de interface do Leanback separa a interface de controles de transporte da o player que reproduz o vídeo. Isso é realizado com dois componentes: um fragmento de suporte à reprodução para exibir os controles de transporte (e opcionalmente o vídeo) e um adaptador de player para encapsular um player de mídia.

Fragmento de reprodução

A atividade da interface do seu app precisa usar uma PlaybackSupportFragment ou um VideoSupportFragment. Ambos contêm os controles de transporte da leanback:

É possível personalizar a ObjectAdapter para aprimorar a interface. Por exemplo, use setAdapter() para adicionar um "vídeos relacionados" linha de comando.

PlayerAdapter

PlayerAdapter é uma classe abstrata que controla o player de mídia subjacente. Os desenvolvedores podem escolher a implementação pré-criada de MediaPlayerAdapter ou a própria implementação dessa classe.

Agrupar as partes

Use um "cola de controle" para conectar o fragmento de reprodução ao player. O botão de relaxamento fornece dois tipos de cola:

agrupador de controle de transporte da leanback

Se você quiser que seu app ofereça suporte à barra de progressão de vídeo, use PlaybackTransportControlGlue:

Você também precisa especificar um "host cola" que vincula a cola à reprodução de imagens, desenha os controles de transporte na IU e mantém o estado deles e transmite os eventos de controle de transporte de volta para o agrupador. O host precisa corresponder ao tipo do fragmento de reprodução. Usar PlaybackSupportFragmentGlueHost com um PlaybackFragment e VideoSupportFragmentGlueHost com um VideoFragment.

Esta é uma ilustração que mostra como as peças do controle de transporte do Flix se encaixam:

agrupador de controle de transporte da leanback

O código que une seu aplicativo deve estar dentro da PlaybackSupportFragment ou VideoSupportFragment que define a interface.

Nos seguintes exemplo, o app cria uma instância de PlaybackTransportControlGlue, dê o nome playerGlue a ele. e conecta o VideoSupportFragment a um MediaPlayerAdapter recém-criado. Como este é um VideoSupportFragment. O código de configuração chama setHost() para anexar um VideoSupportFragmentGlueHost para playerGlue. O código é incluído na classe que estende o 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));
  }
}

O código de configuração também define um PlayerAdapter.Callback para processar eventos do o player de mídia.

Personalizar o agrupador da IU

Você pode personalizar PlaybackBannerControlGlue e PlaybackTransportControlGlue para alterar PlaybackControlsRow

Personalizar o título e a descrição

Para personalizar o título e a descrição na parte superior do controles de mídia, substituir 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;
}

Adicionar controles

O agrupador de controles exibe controles para ações em uma PlaybackControlsRow.

as ações no PlaybackControlsRow; são atribuídas a dois grupos: ações principais e secundárias ações personalizadas. Os controles para o grupo principal aparecem acima da barra de busca e dos controles do grupo secundário aparecem abaixo da barra de busca. Inicialmente, há apenas uma ação principal para o botão reproduzir/pausar e nenhuma ação secundária.

Para adicionar ações aos grupos principal e secundário, substitua 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);
}

Você deve substituir onActionClicked() para processar as novas ações.

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

Em casos especiais, convém implementar seus próprios PlaybackTransportRowPresenter para renderizar controles personalizados e responder a ações de busca usando o PlaybackSeekUi.

Barra de progressão de vídeo

Se seu app usa um VideoSupportFragment e você quer compatibilizar com barra de progressão de vídeo.

como arrastar o marcador

Você precisa fornecer uma implementação de PlaybackSeekDataProvider. Esse componente exibe miniaturas no vídeo que são usadas ao mover a barra. Você precisa implementar seu próprio provedor, estendendo PlaybackSeekDataProvider: Confira o exemplo na App Leanback Showcase (link em inglês). ,