Leanback 전송 컨트롤 사용

Leanback androidx 라이브러리에는 개선된 사용자 환경을 제공하는 새로운 재생 컨트롤이 있습니다. 동영상 앱의 경우 전송 컨트롤은 앞으로/뒤로 컨트롤로 동영상 스크러빙을 지원합니다. 스크러빙하는 동안 디스플레이에 동영상 탐색에 도움이 되는 썸네일이 표시됩니다.

라이브러리에 포함된 추상 클래스와 기본 구현을 통해 개발자는 더 세부적으로 제어할 수 있습니다. 사전 빌드된 구현을 사용하여 많은 코딩 없이도 기능이 풍부한 앱을 간편하게 빌드할 수 있습니다. 맞춤설정이 더 필요하면 라이브러리의 사전 빌드된 구현을 확장하면 됩니다.

컨트롤 및 플레이어

Leanback 라이브러리는 전송 컨트롤이 있는 UI를 동영상을 재생하는 플레이어와 분리합니다. 이렇게 하려면 두 가지 구성요소, 즉 전송 컨트롤 (및 선택사항으로 동영상)을 표시하는 재생 지원 프래그먼트와 미디어 플레이어를 캡슐화하는 플레이어 어댑터가 필요합니다.

재생 프래그먼트

앱의 UI 활동은 PlaybackSupportFragment 또는 VideoSupportFragment를 사용해야 합니다. 둘 다 leanback 전송 컨트롤을 포함하고 있습니다.

프래그먼트의 ObjectAdapter를 맞춤설정하여 UI를 개선할 수 있습니다. 예를 들어 setAdapter()를 사용하여 '관련 동영상' 행을 추가합니다.

PlayerAdapter

PlayerAdapter는 기본 미디어 플레이어를 제어하는 추상 클래스입니다. 개발자는 사전 빌드된 MediaPlayerAdapter 구현을 선택하거나 직접 이 클래스의 구현을 작성할 수 있습니다.

요소 연결

'컨트롤 글루'를 사용하여 재생 프래그먼트를 플레이어에 연결해야 합니다. Leanback 라이브러리는 두 가지 종류의 글루를 제공합니다.

leanback 전송 컨트롤 글루

앱에서 동영상 스크러빙을 지원하려면 PlaybackTransportControlGlue를 사용해야 합니다.

또한 글루를 재생 프래그먼트에 바인딩하고, UI에서 전송 컨트롤을 가져오고, 상태를 유지하며, 전송 컨트롤 이벤트를 다시 글루에 전달하는 '글루 호스트'도 지정해야 합니다. 이 호스트는 재생 프래그먼트 유형과 일치해야 합니다. PlaybackFragment와 함께 PlaybackSupportFragmentGlueHost를 사용하고 VideoFragment와 함께 VideoSupportFragmentGlueHost를 사용합니다.

다음 그림은 leanback 전송 컨트롤의 요소가 어떻게 함께 연결되는지 보여줍니다.

leanback 전송 컨트롤 글루

앱을 묶는 코드는 UI를 정의하는 PlaybackSupportFragment 또는 VideoSupportFragment 내에 있어야 합니다.

다음 예에서 앱은 이름을 playerGlue로 지정하여 PlaybackTransportControlGlue 인스턴스를 생성하고 해당 VideoSupportFragment를 새로 생성된 MediaPlayerAdapter에 연결합니다. VideoSupportFragment이므로 설정 코드는 setHost()를 호출하여 VideoSupportFragmentGlueHostplayerGlue에 연결합니다. 이 코드는 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));
  }
}

설정 코드는 미디어 플레이어의 이벤트를 처리하기 위해 PlayerAdapter.Callback도 정의합니다.

UI 글루 맞춤설정

PlaybackBannerControlGluePlaybackTransportControlGlue를 맞춤설정하여 PlaybackControlsRow를 변경할 수 있습니다.

제목과 설명 맞춤설명

재생 컨트롤 상단에 표시되는 제목과 설명을 맞춤설정하려면 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;
}

컨트롤 추가

컨트롤 글루에서는 PlaybackControlsRow에 작업 컨트롤을 표시합니다.

PlaybackControlsRow의 작업은 두 그룹, 즉 기본 작업보조 작업에 할당됩니다. 기본 그룹용 컨트롤은 탐색 막대 위에, 보조 그룹용 컨트롤은 탐색 막대 아래에 표시됩니다. 처음에는 재생/일시중지 버튼에 관한 기본 작업이 하나만 있고 보조 작업은 없습니다.

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

새 작업을 처리하려면 onActionClicked()를 재정의해야 합니다.

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

특수한 경우 자체 PlaybackTransportRowPresenter를 구현하여 PlaybackSeekUi를 사용하여 맞춤 컨트롤을 렌더링하고 탐색 작업에 응답하는 것이 좋습니다.

동영상 스크러빙

앱에서 VideoSupportFragment를 사용하며 동영상 스크러빙을 지원하려는 경우

스크러빙

PlaybackSeekDataProvider의 구현을 제공해야 합니다. 이 구성요소는 스크롤할 때 사용되는 동영상 미리보기 이미지를 제공합니다. PlaybackSeekDataProvider를 확장하여 고유한 제공자를 구현해야 합니다. Leanback 쇼케이스 앱에서 예시를 참고하세요.