Media3는 몇 가지 맞춤설정 옵션을 제공하는 기본 PlayerView를 제공합니다.
드로어블 재정의
PlayerView는 PlayerControlView를 사용하여 재생 컨트롤과 진행률 표시줄을 표시합니다. PlayerControlView에서 사용하는 드로어블은 애플리케이션에 정의된 동일한 이름의 드로어블로 재정의할 수 있습니다. 재정의할 수 있는 컨트롤 드로어블 목록은 PlayerControlView 문서를 참고하세요.
추가 맞춤설정의 경우 앱 개발자가 자체 UI 구성요소를 구현해야 합니다. 하지만 시작하는 데 도움이 되는 몇 가지 권장사항은 다음과 같습니다.
권장사항
Media3 Player(예: ExoPlayer, MediaController 또는 맞춤 Player 구현)에 연결되는 미디어 UI를 구현할 때 앱은 최상의 UI 환경을 위해 다음 권장사항을 따르는 것이 좋습니다.
재생/일시중지 버튼
재생 및 일시중지 버튼은 단일 플레이어 상태에 직접 대응하지 않습니다. 예를 들어 플레이어가 일시중지되지 않은 경우에도 사용자는 재생이 종료되거나 실패한 후 재생을 다시 시작할 수 있어야 합니다.
구현을 간소화하기 위해 Media3는 표시할 버튼을 결정하는 유틸리티 메서드(Util.shouldShowPlayButton)와 버튼 누르기를 처리하는 유틸리티 메서드(Util.handlePlayPauseButtonAction)를 제공합니다.
Kotlin
val shouldShowPlayButton: Boolean = Util.shouldShowPlayButton(player) playPauseButton.setImageDrawable(if (shouldShowPlayButton) playDrawable else pauseDrawable) playPauseButton.setOnClickListener { Util.handlePlayPauseButtonAction(player) }
Java
boolean shouldShowPlayButton = Util.shouldShowPlayButton(player); playPauseButton.setImageDrawable(shouldShowPlayButton ? playDrawable : pauseDrawable); playPauseButton.setOnClickListener(view -> Util.handlePlayPauseButtonAction(player));
상태 업데이트 수신 대기
UI 구성요소는 상응하는 UI 업데이트가 필요한 상태 변경을 알리기 위해 Player.Listener를 추가해야 합니다. 자세한 내용은 재생 이벤트 수신 대기를 참고하세요.
UI 새로고침은 비용이 많이 들 수 있으며 여러 플레이어 이벤트가 함께 도착하는 경우가 많습니다. 짧은 시간 내에 UI를 너무 자주 새로고침하지 않으려면 일반적으로 onEvents만 수신 대기하고 여기에서 UI 업데이트를 트리거하는 것이 좋습니다.
Kotlin
player.addListener( object : Player.Listener { override fun onEvents(player: Player, events: Player.Events) { if ( events.containsAny( Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, ) ) { updatePlayPauseButton() } if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) { updateRepeatModeButton() } } } )
Java
player.addListener( new Player.Listener() { @Override public void onEvents(Player player, Player.Events events) { if (events.containsAny( Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED)) { updatePlayPauseButton(); } if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) { updateRepeatModeButton(); } } });
사용 가능한 명령어 처리
다양한 Player 구현과 함께 작동해야 할 수 있는 범용 UI 구성요소는 사용 가능한 플레이어 명령어를 확인하여 버튼을 표시하거나 숨기고 지원되지 않는 메서드를 호출하지 않아야 합니다.
Kotlin
nextButton.isEnabled = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT));
첫 프레임 셔터 및 이미지 표시
UI 구성요소는 동영상이나 이미지를 표시할 때 일반적으로 실제 첫 번째 프레임이나 이미지를 사용할 수 있을 때까지 자리표시자 셔터 뷰를 사용합니다. 또한 동영상과 이미지 재생이 혼합된 경우 적절한 시점에 이미지 뷰를 숨기고 표시해야 합니다.
이러한 업데이트를 처리하는 일반적인 패턴은 선택한 트랙(EVENT_TRACKS_CHANGED)의 변경사항과 첫 번째 동영상 프레임이 렌더링된 시점(EVENT_RENDERED_FIRST_FRAME)에 대해 Player.Listener.onEvents()를 수신 대기하고 새 이미지를 사용할 수 있는 시점에 대해 ImageOutput.onImageAvailable()를 수신 대기하는 것입니다.
Kotlin
override fun onEvents(player: Player, events: Player.Events) { if (events.contains(Player.EVENT_TRACKS_CHANGED)) { // If no video or image track: show shutter, hide image view. // Otherwise: do nothing to wait for first frame or image. } if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) { // Hide shutter, hide image view. } } override fun onImageAvailable(presentationTimeUs: Long, bitmap: Bitmap) { // Show shutter, set image and show image view. }
자바
@Override public void onEvents(Player player, Player.Events events) { if (events.contains(Player.EVENT_TRACKS_CHANGED)) { // If no video or image track: show shutter, hide image view. // Otherwise: do nothing to wait for first frame or image. } if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) { // Hide shutter, hide image view. } } @Override public void onImageAvailable(long presentationTimeUs, Bitmap bitmap) { // Show shutter, set image and show image view. }
스크러버 및 탐색 메뉴 컨트롤
ExoPlayer 인스턴스에 연결되는 맞춤 탐색 막대 또는 스크러버를 구현할 때 사용자가 스크러버 엄지손가락을 드래그하기 시작하면 ExoPlayer.setScrubbingModeEnabled를 사용하여 스크러빙 모드를 사용 설정하고 드래그 중에 Player.seekTo 업데이트를 전달하고 동작이 완료되거나 취소되는 즉시 스크러빙 모드를 사용 중지합니다. 정상 재생 또는 단일 탐색 중에는 스크러빙 모드를 사용 설정하지 마세요. 재생이 억제되고 디코더 리소스 사용량이 증가하기 때문입니다.
또한 ExoPlayer가 스크러버의 터치 정밀도 내에서 근처 키프레임으로 스냅할 수 있도록 ScrubbingModeParameters.Builder.setFractionalSeekTolerance를 사용하여 UI 스크러버 너비에서 dp 기반 부분 탐색 허용 오차를 구성하는 것이 좋습니다. 코드 예시 및 세부정보는 스크러빙 모드를 참고하세요.