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

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

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

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

Управление и игрок

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

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

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

  • Объект 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))
  }
}

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));
  }
}

Обратите внимание, что в коде настройки также определен объект PlayerAdapter.Callback для обработки событий от медиаплеера.

Настройка интерфейса пользователя

Вы можете настроить PlaybackBannerControlGlue и PlaybackTransportControlGlue чтобы изменить PlaybackControlsRow .

Настройка заголовка и описания

Чтобы настроить заголовок и описание в верхней части элементов управления воспроизведением, переопределите onCreateRowPresenter() :

Котлин

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

Добавление элементов управления

Элемент управления отображает элементы управления для действий в строке 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)
    }
}

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);
}

Для обработки новых действий необходимо переопределить 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.
}

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

В особых случаях может потребоваться реализовать собственный PlaybackTransportRowPresenter для отображения пользовательских элементов управления и реагирования на действия перемотки с помощью PlaybackSeekUi .

перемотка видео

Если ваше приложение использует VideoSupportFragment и вы хотите поддерживать перемотку видео.

чистка

Вам необходимо предоставить реализацию компонента PlaybackSeekDataProvider . Этот компонент предоставляет миниатюры видео, используемые при прокрутке. Вы должны реализовать свой собственный поставщик, расширив PlaybackSeekDataProvider . См. пример в приложении Leanback Showcase .