توفّر Media3 PlayerView
تلقائيًا يتيح بعض خيارات التخصيص.
تجاوز العناصر القابلة للرسم
يستخدم PlayerView
PlayerControlView
لعرض عناصر التحكّم في التشغيل وشريط التقدم. يمكن تجاهل العناصر القابلة للرسم التي يستخدمها PlayerControlView
من خلال عناصر قابلة للرسم تحمل الأسماء نفسها المحدّدة في تطبيقك. راجِع مستندات
PlayerControlView
للاطّلاع على قائمة بعناصر الرسومات القابلة للرسم الخاصة بعناصر التحكّم التي يمكن إلغاء تعريفها.
لإجراء أي تخصيص إضافي، يُتوقّع من مطوّري التطبيقات تنفيذ مكوّنات واجهة المستخدم الخاصة بهم. ومع ذلك، إليك بعض أفضل الممارسات التي يمكن أن تساعدك في البدء.
أفضل الممارسات
عند تنفيذ واجهة مستخدم وسائط تتصل بـ Media3 Player
(على سبيل المثال
ExoPlayer
أو MediaController
أو تنفيذ Player
مخصّص)، يُنصح التطبيقات باتّباع أفضل الممارسات التالية للحصول على أفضل تجربة لواجهة المستخدم.
زر التشغيل/الإيقاف المؤقت
لا يتوافق زر التشغيل والإيقاف المؤقت بشكل مباشر مع حالة مشغّل واحدة. على سبيل المثال، يجب أن يتمكّن المستخدم من إعادة تشغيل المحتوى بعد انتهائه أو تعذُّر تشغيله، حتى إذا لم يتم إيقاف المشغّل مؤقتًا.
لتسهيل عملية التنفيذ، توفّر Media3 طرقًا مساعدة لتحديد الزر الذي سيتم عرضه (Util.shouldShowPlayButton
) والتعامل مع ضغطات الأزرار (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));
الاستماع إلى آخر الأخبار حول حالة إصلاح الجهاز
يجب أن يضيف مكوّن واجهة المستخدم Player.Listener
ليتم إعلامه بتغييرات الحالة التي تتطلّب تعديلاً مطابقًا في واجهة المستخدم. لمزيد من التفاصيل، يمكنك الاطّلاع على الاستماع إلى أحداث التشغيل.
يمكن أن تكون إعادة تحميل واجهة المستخدم مكلفة، وغالبًا ما تصل أحداث اللاعبين المتعددين معًا. لتجنُّب إعادة تحميل واجهة المستخدم بشكل متكرّر خلال فترة زمنية قصيرة، من الأفضل بشكل عام الاستماع إلى onEvents
وتفعيل عمليات تعديل واجهة المستخدم من هناك:
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(); } } });
التعامل مع الأوامر المتاحة
يجب أن يتحقّق أحد عناصر واجهة المستخدم للأغراض العامة الذي قد يحتاج إلى العمل مع عمليات تنفيذ مختلفة Player
من أوامر المشغّل المتاحة لعرض الأزرار أو إخفائها ولتجنُّب استدعاء طرق غير متوافقة:
Kotlin
nextButton.isEnabled = player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)
Java
nextButton.setEnabled(player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT));
مصراع الإطار الأول وعرض الصورة
عندما يعرض أحد عناصر واجهة المستخدم فيديو أو صورًا، يستخدم عادةً عرضًا مؤقتًا للمصراع إلى أن يتوفّر الإطار الأول أو الصورة الفعلية. بالإضافة إلى ذلك، يتطلّب تشغيل الفيديو والصورة معًا إخفاء عرض الصورة وإظهاره في الأوقات المناسبة.
يتم عادةً التعامل مع هذه التعديلات من خلال الاستماع إلى
Player.Listener.onEvents()
لرصد أي تغيير في المقاطع الصوتية المحدّدة
(EVENT_TRACKS_CHANGED
) وعند عرض إطار الفيديو الأول
(EVENT_RENDERED_FIRST_FRAME
)، بالإضافة إلى 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. }