El kit de herramientas de la IU de Leanback tiene controles de reproducción que proporcionan una experiencia del usuario mejorada. 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 se arrastra la pantalla, se muestran miniaturas para ayudar 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. Con las implementaciones compiladas previamente, puedes compilar rápidamente una app con muchas funciones sin tener que programar demasiado. Si necesitas una mayor personalización, puedes extender cualquiera de los componentes precompilados de la biblioteca.
Controles y reproductor
El kit de herramientas de la IU de Leanback separa la IU de controles de transporte del 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 del reproductor para encapsular un reproductor multimedia.
Fragmento de reproducción
La actividad de IU de tu app debe usar un PlaybackSupportFragment
o VideoSupportFragment
.
Ambos contienen los controles de transporte de Leanback.
- Un
PlaybackSupportFragment
anima sus controles de transporte para ocultarlos o mostrarlos, según sea necesario. - Un
VideoSupportFragment
extiendePlaybackSupportFragment
y tiene unSurfaceView
para procesar el video.
Puedes personalizar el ObjectAdapter
de un fragmento para mejorar la IU. Por ejemplo, usa setAdapter()
para agregar una fila de "videos relacionados".
PlayerAdapter
PlayerAdapter
es una clase abstracta que controla el reproductor multimedia subyacente. Los desarrolladores pueden elegir la implementación de MediaPlayerAdapter
compilada previamente o escribir su propia implementación de esta clase.
Cómo unir las piezas
Debes usar alguna "unión de controles" para conectar el fragmento de reproducción con el reproductor. La biblioteca Leanback proporciona dos tipos de unión:
PlaybackBannerControlGlue
dibuja los controles de transporte en el fragmento de reproducción en el "estilo antiguo" y los coloca dentro de un fondo opaco. (PlaybackBannerControlGlue
reemplaza aPlaybackControlGlue
, que dejó de estar disponible).PlaybackTransportControlGlue
usa controles de "diseño nuevo" con un fondo transparente.
Si quieres que tu app admita la limpieza de videos, debes usar PlaybackTransportControlGlue
.
También debes especificar un "host de unión" que la vincule con el fragmento de reproducción, dibuje los controles de transporte de la IU, mantenga su estado y pase los eventos de control de transporte de vuelta a la unión. El host debe coincidir con el tipo de fragmento de reproducción. Usa PlaybackSupportFragmentGlueHost
con un PlaybackFragment
y VideoSupportFragmentGlueHost
con un VideoFragment
.
A continuación, se incluye una ilustración que muestra cómo se combinan las piezas de un control de transporte de Leanback:
El código que une tu app debe estar dentro del PlaybackSupportFragment
o VideoSupportFragment
que define la IU.
En el siguiente ejemplo, la app crea una instancia de PlaybackTransportControlGlue
, la denomina playerGlue
y conecta su VideoSupportFragment
a un MediaPlayerAdapter
recién creado. Como 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 extiende 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
para controlar eventos del 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 de la parte superior de los controles de reproducción, anula 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 de PlaybackControlsRow
se asignan a dos grupos: acciones primarias y acciones secundarias. Los controles del grupo primario aparecen sobre la barra de búsqueda y los controles del grupo secundario aparecen debajo de ella. Inicialmente, solo hay una 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 si anulas 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 administrar 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 desees implementar tu propio PlaybackTransportRowPresenter
para renderizar controles personalizados y responder a las acciones de búsqueda con PlaybackSeekUi
.
Cómo hacer arrastre de video
Si tu app usa un VideoSupportFragment
y quieres que sea compatible con el arrastre de videos:
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.