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

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

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

عند تنفيذ واجهة مستخدم للوسائط تتصل بعنصر 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.
}