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

توفّر 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(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.
}