Cómo usar los controles de transporte

Compila mejor con Compose
Crea IU atractivas con muy poco código usando Jetpack Compose para el SO Android TV.

El kit de herramientas de IU de Leanback tiene controles de reproducción que proporcionan una la experiencia del usuario. En el caso de las apps de video, los controles de transporte admiten el arrastre de video. con los controles de avance y retroceso. Mientras arrastras la pantalla, se muestran miniaturas para ayudarte a navegar por el video.

La biblioteca incluye clases abstractas, así como implementaciones compiladas previamente y listas para usar que proporcionan un control más detallado a los desarrolladores. Cómo usar la compilación implementaciones, puedes crear rápidamente una app con muchas funciones sin tener que programar. Si necesitas una mayor personalización, puedes extender cualquiera de los componentes o los componentes de la solución.

Controles y reproductor

El kit de herramientas de IU de Leanback separa la IU de controles de transporte de el reproductor que reproduce el video. Esto se logra con dos componentes: un fragmento de compatibilidad de reproducción para mostrar los controles de transporte (y opcionalmente el video) y un adaptador de reproductor para encapsular un reproductor multimedia.

Fragmento de reproducción

La actividad de IU de tu app debe usar una PlaybackSupportFragment o un VideoSupportFragment Ambos contienen los controles de transporte de Leanback.

Puedes personalizar la configuración ObjectAdapter para mejorar la IU. Por ejemplo, usa setAdapter() agregar "videos relacionados" fila.

PlayerAdapter

PlayerAdapter es una clase abstracta que controla el reproductor multimedia subyacente. Los desarrolladores pueden elegir la implementación de MediaPlayerAdapter compilada previamente, o escribe su propia implementación de esta clase.

Cómo unir las piezas

Debes usar un poco de "unión de controles" para conectar el fragmento de reproducción al reproductor. La función Leanback proporciona dos tipos de unión:

unión de control de transporte de Leanback

Si quieres que tu app admita la limpieza de videos, debes usar PlaybackTransportControlGlue

También debes especificar un “host de unión” que une el pegamento a la reproducción dibuja los controles de transporte en la IU y mantiene su estado, y y vuelve a pasar los eventos de control de transporte a la unión. El host debe coincidir con el tipo de fragmento de reproducción. Usa PlaybackSupportFragmentGlueHost con una PlaybackFragment y VideoSupportFragmentGlueHost con un VideoFragment

Esta es una ilustración que muestra cómo las partes de un control de transporte de Leanback encajan:

unión de control de transporte de Leanback

El código que une tu app debe estar dentro del PlaybackSupportFragment o VideoSupportFragment que define la IU.

En la siguiente Por ejemplo, la app construye una instancia de PlaybackTransportControlGlue, llamándolo playerGlue, y conecta su VideoSupportFragment a un MediaPlayerAdapter recién creado. Desde Este es un VideoSupportFragment; el código de configuración llama a setHost() para adjuntar un VideoSupportFragmentGlueHost a playerGlue. El código se incluye dentro de la clase que extienda 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));
  }
}

Ten en cuenta que el código de configuración también define un PlayerAdapter.Callback desde el cual controlar eventos el reproductor multimedia.

Cómo personalizar la unión de IU

Puedes personalizar PlaybackBannerControlGlue y PlaybackTransportControlGlue para cambiar PlaybackControlsRow

Cómo personalizar el título y la descripción

Para personalizar el título y la descripción en la parte superior de la controles de reproducción, anular 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;
}

Cómo agregar controles

La unión de controles muestra controles para acciones en un PlaybackControlsRow.

Las acciones en PlaybackControlsRow se asignan a dos grupos: acciones primarias y secundarias. acciones. Los controles del grupo primario aparecen sobre la barra deslizante y los controles para el grupo secundario aparecen debajo de la barra deslizante. Inicialmente, hay una sola acción principal para el botón de reproducción/pausa, y ninguna acción secundaria.

Puedes agregar acciones a los grupos primario y secundario anulando onCreatePrimaryActions() y 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);
}

Debes anular onActionClicked() para controlar las acciones nuevas

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

En casos especiales, es posible que quieras implementar tu propio PlaybackTransportRowPresenter para renderizar controles personalizados y responder a las acciones de búsqueda con el PlaybackSeekUi

Cómo hacer arrastre de video

Si tu app usa un VideoSupportFragment y quieres que sea compatible con el arrastre de videos:

limpieza

Tú debes proporcionar una implementación de PlaybackSeekDataProvider. Este componente proporciona miniaturas del video durante el desplazamiento. Debes implementar tu propio proveedor extendiendo PlaybackSeekDataProvider Consulta el ejemplo en la App de Leanback Showcase. de Google Cloud.