Media3는 몇 가지 맞춤설정 옵션을 제공하는 기본 PlayerView
를 제공합니다. 추가 맞춤설정을 위해 앱 개발자는 자체 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) }
자바
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() } } })
자바
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(Player.COMMAND_SEEK_TO_NEXT)
자바
nextButton.setEnabled(player.isCommandAvailable(Player.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. }
Java
@Override public void onEvents(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. }