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 menerapkan UI media yang terhubung ke Player
Media3 (misalnya
ExoPlayer
, MediaController
, atau penerapan Player
kustom), aplikasi disarankan untuk mengikuti praktik terbaik berikut untuk pengalaman UI terbaik.
Tombol Putar/Jeda
Tombol putar dan jeda tidak secara langsung sesuai dengan status pemain tunggal. Misalnya, pengguna harus dapat memulai ulang pemutaran setelah berakhir atau gagal meskipun pemutar tidak dijeda.
Untuk menyederhanakan penerapan, Media3 menyediakan metode util untuk memutuskan tombol mana 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 pembaruan status
Komponen UI perlu menambahkan Player.Listener
untuk diberi tahu tentang perubahan status yang memerlukan update UI yang sesuai. Lihat Mendengarkan peristiwa pemutaran untuk mengetahui detailnya.
Memuat ulang UI dapat memakan biaya dan beberapa peristiwa pemain sering kali tiba bersamaan. Untuk menghindari terlalu sering me-refresh UI dalam waktu singkat, umumnya lebih baik memproses onEvents
saja dan memicu 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 serbaguna 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(Player.COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));
Shutter frame pertama dan tampilan gambar
Saat menampilkan video atau gambar, komponen UI biasanya menggunakan tampilan rana placeholder hingga frame atau gambar pertama yang sebenarnya tersedia. Selain itu, pemutaran video dan gambar campuran mengharuskan tampilan gambar disembunyikan dan ditampilkan pada waktu yang tepat.
Pola umum untuk menangani update ini adalah dengan memantau
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, 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. }