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

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

В наборе инструментов Leanback UI имеются элементы управления воспроизведением, которые повышают удобство работы пользователя. Для видеоприложений элементы управления транспортировкой поддерживают прокрутку видео с помощью элементов управления вперед и назад. При прокрутке экрана отображаются миниатюры, помогающие перемещаться по видео.

Библиотека включает в себя абстрактные классы, а также готовые готовые реализации, которые обеспечивают более детальный контроль для разработчиков. Используя готовые реализации, вы можете быстро создать многофункциональное приложение без большого количества кода. Если вам нужны дополнительные настройки, вы можете расширить любой из готовых компонентов библиотеки.

Управление и плеер

Набор инструментов Leanback UI отделяет пользовательский интерфейс управления транспортировкой от проигрывателя, воспроизводящего видео. Это достигается с помощью двух компонентов: фрагмента поддержки воспроизведения для отображения элементов управления транспортировкой (и, возможно, видео) и адаптера проигрывателя для инкапсуляции медиаплеера.

Фрагмент воспроизведения

Активность пользовательского интерфейса вашего приложения должна использовать PlaybackSupportFragment или VideoSupportFragment . Оба содержат элементы управления транспортировкой:

  • PlaybackSupportFragment анимирует элементы управления транспортировкой, чтобы скрыть/показать их по мере необходимости.
  • VideoSupportFragment расширяет PlaybackSupportFragment и имеет SurfaceView для рендеринга видео.

Вы можете настроить ObjectAdapter фрагмента для улучшения пользовательского интерфейса. Например, используйте setAdapter() , чтобы добавить строку «похожие видео».

Адаптер игрока

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