Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

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 내에 있어야 합니다.

다음 예에서 앱은 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))
      }
    }
    

자바

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

자바

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

자바

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

자바

    @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를 확장하여 고유한 제공자를 구현해야 합니다. Android TV GitHub 저장소에서 Android Leanback Showcase 샘플 앱의 예를 참조하세요.