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

يوفّر 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 أوامر المشغّل المتاحة لعرض buttons أو إخفائها وتجنُّب استدعاء طرق غير متوافقة:

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.
}