Инструментарий Leanback UI включает элементы управления воспроизведением, которые улучшают пользовательский интерфейс. В видеоприложениях элементы управления воспроизведением поддерживают перемотку видео с помощью кнопок «вперёд» и «назад». При перемотке на экране отображаются миниатюры, упрощающие навигацию по видео.
Библиотека включает в себя абстрактные классы, а также готовые реализации, предоставляющие разработчикам более детальный контроль. Используя готовые реализации, вы можете быстро создать многофункциональное приложение без необходимости написания большого количества кода. Если вам потребуется дополнительная настройка, вы можете расширить любой из готовых компонентов библиотеки.
Управление и плеер
Набор инструментов Leanback UI отделяет пользовательский интерфейс управления воспроизведением от проигрывателя, воспроизводящего видео. Это достигается с помощью двух компонентов: фрагмента поддержки воспроизведения для отображения элементов управления воспроизведением (и, при необходимости, видео) и адаптера проигрывателя для инкапсуляции медиаплеера.
Фрагмент воспроизведения
UI Activity вашего приложения должен использовать PlaybackSupportFragment
или VideoSupportFragment
. Оба содержат элементы управления Leanback Transport:
-
PlaybackSupportFragment
анимирует свои элементы управления транспортировкой, скрывая/отображая их по мере необходимости. -
VideoSupportFragment
расширяетPlaybackSupportFragment
и имеетSurfaceView
для рендеринга видео.
Вы можете настроить ObjectAdapter
фрагмента для улучшения пользовательского интерфейса. Например, используйте setAdapter()
, чтобы добавить строку «Похожие видео».
PlayerAdapter
PlayerAdapter
— это абстрактный класс, управляющий базовым медиаплеером. Разработчики могут выбрать готовую реализацию MediaPlayerAdapter
или написать собственную.
Склеивание деталей вместе
Для соединения фрагмента воспроизведения с плеером необходимо использовать «контрольный клей». Библиотека Leanback предоставляет два вида клея:
-
PlaybackBannerControlGlue
рисует элементы управления воспроизведением во фрагменте воспроизведения в «старом стиле», помещая их на непрозрачный фон. (PlaybackBannerControlGlue
заменяетPlaybackControlGlue
, который устарел.) -
PlaybackTransportControlGlue
использует элементы управления «нового стиля» с прозрачным фоном.
Если вы хотите, чтобы ваше приложение поддерживало перемотку видео, вы должны использовать PlaybackTransportControlGlue
.
Также необходимо указать «связующий узел», который связывает связующий узел с фрагментом воспроизведения, отображает элементы управления перемещением в пользовательском интерфейсе, поддерживает их состояние и передаёт события управления перемещением обратно связующему узлу. Тип узла должен соответствовать типу фрагмента воспроизведения. Используйте PlaybackSupportFragmentGlueHost
с PlaybackFragment
и VideoSupportFragmentGlueHost
с VideoFragment
.
Ниже приведена иллюстрация, показывающая, как соединяются между собой части элемента управления наклонной транспортировкой:
Код, который объединяет ваше приложение, должен находиться внутри PlaybackSupportFragment
или VideoSupportFragment
, определяющего пользовательский интерфейс.
В следующем примере приложение создаёт экземпляр PlaybackTransportControlGlue
, называя его playerGlue
, и подключает его VideoSupportFragment
к недавно созданному MediaPlayerAdapter
. Поскольку это VideoSupportFragment
, код настройки вызывает setHost()
для присоединения VideoSupportFragmentGlueHost
к playerGlue
. Этот код включён в класс, расширяющий VideoSupportFragment
.
Котлин
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)) } }
Ява
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)); } }
Обратите внимание, что код настройки также определяет PlayerAdapter.Callback
для обработки событий от медиаплеера.
Настройка связующего элемента пользовательского интерфейса
Вы можете настроить PlaybackBannerControlGlue
и PlaybackTransportControlGlue
для изменения PlaybackControlsRow
.
Настройка заголовка и описания
Чтобы настроить заголовок и описание в верхней части элементов управления воспроизведением, переопределите onCreateRowPresenter()
:
Котлин
override fun onCreateRowPresenter(): PlaybackRowPresenter { return super.onCreateRowPresenter().apply { (this as? PlaybackTransportRowPresenter) ?.setDescriptionPresenter(MyCustomDescriptionPresenter()) } }
Ява
@Override protected PlaybackRowPresenter onCreateRowPresenter() { PlaybackTransportRowPresenter presenter = (PlaybackTransportRowPresenter) super.onCreateRowPresenter(); presenter.setDescriptionPresenter(new MyCustomDescriptionPresenter()); return presenter; }
Добавление элементов управления
Элемент управления отображает элементы управления для действий в PlaybackControlsRow
.
Действия в строке PlaybackControlsRow
распределены по двум группам: основные и дополнительные . Элементы управления основной группы отображаются над полосой прокрутки, а элементы управления дополнительной группы — под ней. Изначально для кнопки воспроизведения/паузы предусмотрено только одно основное действие, а дополнительных действий нет.
Вы можете добавить действия к первичной и вторичной группам, переопределив onCreatePrimaryActions()
и onCreateSecondaryActions()
.
Котлин
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) } }
Ява
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); }
Для обработки новых действий необходимо переопределить onActionClicked()
.
Котлин
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. }
Ява
@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. }
В особых случаях вам может потребоваться реализовать собственный PlaybackTransportRowPresenter
для визуализации пользовательских элементов управления и реагирования на действия поиска с помощью PlaybackSeekUi
.
Очистка видео
Если ваше приложение использует VideoSupportFragment
и вы хотите поддерживать перемотку видео.
Вам необходимо предоставить реализацию PlaybackSeekDataProvider
. Этот компонент предоставляет миниатюры видео, используемые при прокрутке. Вам необходимо реализовать собственный поставщик, расширив PlaybackSeekDataProvider
. См. пример в приложении Leanback Showcase .