전송 컨트롤 사용

Compose를 사용하여 더 효과적으로 빌드
Android TV OS용 Jetpack Compose를 사용하여 최소한의 코드로 멋진 UI를 만드세요.

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

라이브러리에는 추상 클래스뿐만 아니라 개발자에게 더 세부적인 제어를 제공하는 사전 빌드된 즉시 사용 가능한 구현이 포함되어 있습니다. 사전 빌드된 구현을 사용하면 코딩을 많이 하지 않고도 기능이 풍부한 앱을 빠르게 빌드할 수 있습니다. 추가 맞춤설정이 필요한 경우 라이브러리의 미리 빌드된 구성요소를 확장하면 됩니다.

컨트롤 및 플레이어

Leanback UI 툴킷은 전송 컨트롤 UI를 동영상을 재생하는 플레이어와 분리합니다. 이 작업은 전송 컨트롤 (및 선택적으로 동영상)을 표시하기 위한 재생 지원 프래그먼트와 미디어 플레이어를 캡슐화하는 플레이어 어댑터, 이렇게 두 가지 구성요소를 사용하여 실행됩니다.

재생 프래그먼트

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

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

PlayerAdapter

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

요소 연결

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

leanback 전송 컨트롤 글루

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

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

다음 그림은 leanback 전송 컨트롤의 요소가 어떻게 조화를 이루는지 보여줍니다.

leanback 전송 컨트롤 글루

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

다음 예에서 앱은 PlaybackTransportControlGlue의 인스턴스를 생성하고 이름을 playerGlue로 지정하고 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.
}

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

동영상 스크러빙

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

스크러빙

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