PlayerView는 PlayerControlView를 사용하여 재생 컨트롤과 진행률 표시줄을 표시합니다. PlayerControlView에서 사용하는 드로어블은 애플리케이션에 정의된 이름이 같은 드로어블로 재정의할 수 있습니다. 재정의할 수 있는 제어 드로어블 목록은 PlayerControlView 문서를 참고하세요.
추가 맞춤설정의 경우 앱 개발자는 자체 UI 구성요소를 구현해야 합니다. 다음은 시작하는 데 도움이 되는 몇 가지 권장사항입니다.
권장사항
Media3 Player (예: ExoPlayer, MediaController 또는 맞춤 Player 구현)에 연결되는 미디어 UI를 구현할 때는 최상의 UI 환경을 위해 다음 권장사항을 따르는 것이 좋습니다.
재생/일시중지 버튼
재생 및 일시중지 버튼은 단일 플레이어 상태에 직접 대응하지 않습니다.
예를 들어 플레이어가 일시중지되지 않은 경우에도 사용자는 재생이 종료되거나 실패한 후 재생을 다시 시작할 수 있어야 합니다.
구현을 간소화하기 위해 Media3에서는 표시할 버튼 (Util.shouldShowPlayButton)을 결정하고 버튼 누름(Util.handlePlayPauseButtonAction)을 처리하는 유틸리티 메서드를 제공합니다.
overridefunonEvents(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.}}overridefunonImageAvailable(presentationTimeUs:Long,bitmap:Bitmap){// Show shutter, set image and show image view.}
Java
@OverridepublicvoidonEvents(Playerplayer,Eventsevents){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.}}@OverridepublicvoidonImageAvailable(longpresentationTimeUs,Bitmapbitmap){// Show shutter, set image and show image view.}
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-08-27(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-08-27(UTC)"],[],[],null,["Media3 provides a default [`PlayerView`](/guide/topics/media/ui/playerview) that provides some customization\noptions.\n\nOverride drawables **Caution:** We don't guarantee that the customizations described in the following section will continue to work in future versions of the library. The resource IDs may change name, or some may be deleted entirely. This is indicated by the [resource IDs being marked 'private'](/studio/projects/android-library#PrivateResources).\n\n`PlayerView` uses `PlayerControlView` to display the playback controls and\nprogress bar. The drawables used by `PlayerControlView` can be overridden by\ndrawables with the same names defined in your application. See the\n[`PlayerControlView`](/reference/androidx/media3/ui/PlayerControlView) documentation for a list of control drawables that can\nbe overridden.\n\nFor any further customization, app developers are expected to implement their\nown UI components. However, here are some best practices that can help you get\nstarted.\n\nBest practices\n\nWhen implementing a media UI that connects to a Media3 `Player` (for example\n`ExoPlayer`, `MediaController` or a custom `Player` implementation), apps are\nadvised to follow these best practices for the best UI experience.\n\nPlay/Pause button\n\nThe play and pause button does not directly correspond to a single player state.\nFor example, a user should be able to restart playback after it ended or failed\neven if the player isn't paused.\n\nTo simplify the implementation, Media3 provides util methods to decide which\nbutton to show (`Util.shouldShowPlayButton`) and to handle button presses\n(`Util.handlePlayPauseButtonAction`): \n\nKotlin \n\n```kotlin\nval shouldShowPlayButton: Boolean = Util.shouldShowPlayButton(player)\nplayPauseButton.setImageDrawable(if (shouldShowPlayButton) playDrawable else pauseDrawable)\nplayPauseButton.setOnClickListener { Util.handlePlayPauseButtonAction(player) }\n```\n\nJava \n\n```java\nboolean shouldShowPlayButton = Util.shouldShowPlayButton(player);\nplayPauseButton.setImageDrawable(shouldShowPlayButton ? playDrawable : pauseDrawable);\nplayPauseButton.setOnClickListener(view -\u003e Util.handlePlayPauseButtonAction(player));\n```\n\n\u003cbr /\u003e\n\nListen to state updates\n\nThe UI component needs to add a `Player.Listener` to be informed of state\nchanges that require a corresponding UI update. See [Listen to playback\nevents](/guide/topics/media/exoplayer/listening-to-player-events) for details.\n\nRefreshing the UI can be costly and multiple player events often arrive\ntogether. To avoid refreshing the UI too often in a short period of time, it's\ngenerally better to listen to just `onEvents` and trigger UI updates from there: \n\nKotlin \n\n```kotlin\nplayer.addListener(object : Player.Listener{\n override fun onEvents(player: Player, events: Player.Events){\n if (events.containsAny(\n Player.EVENT_PLAY_WHEN_READY_CHANGED,\n Player.EVENT_PLAYBACK_STATE_CHANGED,\n Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED)) {\n updatePlayPauseButton()\n }\n if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) {\n updateRepeatModeButton()\n }\n }\n})\n```\n\nJava \n\n```java\nplayer.addListener(new Player.Listener() {\n @Override\n public void onEvents(Player player, Player.Events events) {\n if (events.containsAny(\n Player.EVENT_PLAY_WHEN_READY_CHANGED,\n Player.EVENT_PLAYBACK_STATE_CHANGED,\n Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED)) {\n updatePlayPauseButton();\n }\n if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) {\n updateRepeatModeButton();\n }\n }\n});\n```\n\n\u003cbr /\u003e\n\nHandle available commands\n\nA general purpose UI component that may need to work with different `Player`\nimplementations should check the available player commands to show or hide\nbuttons and to avoid calling unsupported methods: \n\nKotlin \n\n```kotlin\nnextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)\n```\n\nJava \n\n```java\nnextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));\n```\n\n\u003cbr /\u003e\n\nFirst frame shutter and image display\n\nWhen a UI component displays video or images, it typically uses a placeholder\nshutter view until the real first frame or image is available. In addition,\nmixed video and image playback requires to hide and show the image view at\nappropriate times.\n\nA common pattern to handle these updates is to listen to\n[`Player.Listener.onEvents()`](/reference/androidx/media3/exoplayer/Player.Listener#onEvents(androidx.media3.exoplayer.Player,%20androidx.media3.exoplayer.Player.Events)) for any change in selected tracks\n(`EVENT_TRACKS_CHANGED`) and for when the first video frame has been rendered\n(`EVENT_RENDERED_FIRST_FRAME`), as well as [`ImageOutput.onImageAvailable()`](/reference/androidx/media3/ui/ImageOutput#onImageAvailable(long,%20android.graphics.Bitmap))\nfor when a new image is available: \n\nKotlin \n\n```kotlin\noverride fun onEvents(player: Player, events: Player.Events) {\n if (events.contains(Player.EVENT_TRACKS_CHANGED)) {\n // If no video or image track: show shutter, hide image view.\n // Otherwise: do nothing to wait for first frame or image.\n }\n if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) {\n // Hide shutter, hide image view.\n }\n}\n\noverride fun onImageAvailable(presentationTimeUs: Long, bitmap: Bitmap) {\n // Show shutter, set image and show image view.\n}\n```\n\nJava \n\n```java\n@Override\npublic void onEvents(Player player, Events events) {\n if (events.contains(Player.EVENT_TRACKS_CHANGED)) {\n // If no video or image track: show shutter, hide image view.\n // Otherwise: do nothing to wait for first frame or image.\n }\n if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) {\n // Hide shutter, hide image view.\n }\n}\n\n@Override\npublic void onImageAvailable(long presentationTimeUs, Bitmap bitmap) {\n // Show shutter, set image and show image view.\n}\n```\n\n\u003cbr /\u003e"]]