Media3 یک PlayerView پیشفرض ارائه میدهد که برخی گزینههای سفارشیسازی را در اختیار قرار میدهد.
PlayerView از PlayerControlView برای نمایش کنترلهای پخش و نوار پیشرفت استفاده میکند. drawableهای مورد استفاده توسط PlayerControlView میتوانند توسط drawableهایی با همان نامهای تعریف شده در برنامه شما بازنویسی شوند. برای مشاهده لیستی از drawableهای کنترلی که میتوانند بازنویسی شوند، به مستندات PlayerControlView مراجعه کنید.
برای هرگونه سفارشیسازی بیشتر، انتظار میرود توسعهدهندگان برنامه، اجزای رابط کاربری خود را پیادهسازی کنند. با این حال، در اینجا چند روش برتر وجود دارد که میتواند به شما در شروع کار کمک کند.
هنگام پیادهسازی رابط کاربری رسانهای که به یک Player Media3 متصل میشود (برای مثال ExoPlayer ، MediaController یا یک پیادهسازی Player سفارشی)، به برنامهها توصیه میشود که برای بهترین تجربه رابط کاربری، از این بهترین شیوهها پیروی کنند.
دکمههای پخش و مکث مستقیماً به وضعیت یک بازیکن اشاره ندارند. برای مثال، کاربر باید بتواند پس از پایان یا عدم موفقیت پخش، حتی اگر پخش متوقف نشده باشد، آن را مجدداً شروع کند.
برای سادهسازی پیادهسازی، Media3 متدهای util را برای تصمیمگیری در مورد نمایش دکمه ( Util.shouldShowPlayButton ) و مدیریت فشرده شدن دکمهها ( Util.handlePlayPauseButtonAction ) ارائه میدهد:
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));
کامپوننت رابط کاربری برای اطلاع از تغییرات وضعیتی که نیاز به بهروزرسانی رابط کاربری مربوطه دارند، باید یک Player.Listener اضافه کند. برای جزئیات بیشتر به رویدادهای Listen to playback مراجعه کنید.
بهروزرسانی رابط کاربری میتواند پرهزینه باشد و رویدادهای متعدد بازیکن اغلب با هم اتفاق میافتند. برای جلوگیری از بهروزرسانی مکرر رابط کاربری در مدت زمان کوتاه، معمولاً بهتر است فقط به onEvents گوش دهید و بهروزرسانیهای رابط کاربری را از آنجا فعال کنید:
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 باشد، باید دستورات موجود برای نمایش یا پنهان کردن دکمهها و جلوگیری از فراخوانی متدهای پشتیبانی نشده را بررسی کند:
nextButton.isEnabled = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT)
nextButton.setEnabled(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT));
وقتی یک کامپوننت رابط کاربری ویدیو یا تصاویر را نمایش میدهد، معمولاً از یک نمای شاتر موقت استفاده میکند تا زمانی که اولین فریم یا تصویر واقعی در دسترس باشد. علاوه بر این، پخش ترکیبی ویدیو و تصویر نیاز به پنهان کردن و نمایش نمای تصویر در زمانهای مناسب دارد.
یک الگوی رایج برای مدیریت این بهروزرسانیها، گوش دادن به Player.Listener.onEvents() برای هرگونه تغییر در آهنگهای انتخابشده ( EVENT_TRACKS_CHANGED ) و برای زمانی که اولین فریم ویدیو رندر شده است ( EVENT_RENDERED_FIRST_FRAME ) و همچنین ImageOutput.onImageAvailable() برای زمانی که یک تصویر جدید در دسترس است، میباشد:
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, 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. }