Используйте средства управления транспортом

Создавайте лучше с помощью Compose
Создавайте красивые пользовательские интерфейсы с минимальным кодом с помощью Jetpack Compose для ОС Android TV.

Инструментарий 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 .