Media3 menyediakan PlayerView default yang menyediakan beberapa opsi penyesuaian.
Mengganti drawable
PlayerView menggunakan PlayerControlView untuk menampilkan kontrol pemutaran dan
status progres. Drawable yang digunakan oleh PlayerControlView dapat diganti dengan
drawable dengan nama yang sama yang ditentukan dalam aplikasi Anda. Lihat dokumentasi PlayerControlView untuk mengetahui daftar drawable kontrol yang dapat diganti.
Untuk penyesuaian lebih lanjut, developer aplikasi diharapkan menerapkan komponen UI mereka sendiri. Namun, berikut beberapa praktik terbaik yang dapat membantu Anda memulai.
Praktik terbaik
Saat mengimplementasikan UI media yang terhubung ke Player Media3 (misalnya
ExoPlayer, MediaController, atau implementasi Player kustom), aplikasi
disarankan untuk mengikuti praktik terbaik ini guna mendapatkan pengalaman UI terbaik.
Tombol Putar/Jeda
Tombol putar dan jeda tidak secara langsung sesuai dengan satu status pemutar. Misalnya, pengguna harus dapat memulai ulang pemutaran setelah berakhir atau gagal, meskipun pemutar tidak dijeda.
Untuk menyederhanakan implementasi, Media3 menyediakan metode util untuk menentukan tombol
yang akan ditampilkan (Util.shouldShowPlayButton) dan untuk menangani penekanan tombol
(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));
Memproses perubahan status
Komponen UI perlu menambahkan Player.Listener agar diberi tahu tentang perubahan status
yang memerlukan update UI yang sesuai. Lihat Memproses peristiwa pemutaran untuk mengetahui detailnya.
Me-refresh UI dapat memerlukan biaya yang besar dan beberapa peristiwa pemain sering kali terjadi
bersamaan. Untuk menghindari terlalu sering me-refresh UI dalam waktu singkat, sebaiknya
proses hanya onEvents dan picu update UI dari sana:
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(); } } });
Menangani perintah yang tersedia
Komponen UI tujuan umum yang mungkin perlu berfungsi dengan berbagai implementasi Player
harus memeriksa perintah pemutar yang tersedia untuk menampilkan atau menyembunyikan
tombol dan untuk menghindari pemanggilan metode yang tidak didukung:
Kotlin
nextButton.isEnabled = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT));
Shutter frame pertama dan tampilan gambar
Saat komponen UI menampilkan video atau gambar, komponen tersebut biasanya menggunakan tampilan shutter placeholder hingga frame atau gambar pertama yang sebenarnya tersedia. Selain itu, pemutaran video dan gambar campuran memerlukan penyembunyian dan penampilan tampilan gambar pada waktu yang tepat.
Pola umum untuk menangani update ini adalah memproses
Player.Listener.onEvents() untuk setiap perubahan pada trek yang dipilih
(EVENT_TRACKS_CHANGED) dan saat frame video pertama telah dirender
(EVENT_RENDERED_FIRST_FRAME), serta ImageOutput.onImageAvailable()
saat gambar baru tersedia:
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, 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. }