تخصيصات واجهة المستخدم

توفر مكتبة Media3 عنصر PlayerView تلقائيًا يقدّم بعض خيارات التخصيص.

تجاوز العناصر القابلة للرسم

يستخدم PlayerView عنصر PlayerControlView لعرض عناصر التحكّم في التشغيل وشريط التقدّم. يمكن تجاوز العناصر القابلة للرسم التي يستخدمها PlayerControlView من خلال عناصر قابلة للرسم تحمل الأسماء نفسها المحدّدة في تطبيقك. يُرجى الاطّلاع على مستندات PlayerControlView للحصول على قائمة بعناصر التحكّم القابلة للرسم التي يمكن تجاوزها.

بالنسبة إلى أي تخصيص إضافي، يُفترض أن ينفّذ مطوّرو التطبيقات مكوّنات واجهة المستخدم الخاصة بهم. في ما يلي بعض أفضل الممارسات التي يمكن أن تساعدك في البدء.

أفضل الممارسات

عند تنفيذ واجهة مستخدم للوسائط تتصل بعنصر Player في Media3 (مثل 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(COMMAND_SEEK_TO_NEXT)

Java

nextButton.setEnabled(player.isCommandAvailable(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, 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.
}