إضافة فيديوهات باستخدام ميزة "نافذة ضمن النافذة" (PiP)

تجربة ميزة "الكتابة"
‫Jetpack Compose هي مجموعة أدوات واجهة المستخدم المُقترَحة لنظام التشغيل Android. تعرَّف على كيفية إتاحة وضع "نافذة ضمن النافذة" في ميزة "الإنشاء".

بدءًا من Android 8.0 (المستوى 26 من واجهة برمجة التطبيقات)، يسمح Android بتشغيل الأنشطة في وضع "نافذة ضمن النافذة". "وضع النافذة ضمن النافذة" هو نوع خاص من وضع "النوافذ المتعددة" ويُستخدَم في أغلب الأحيان لتشغيل الفيديو. تتيح هذه الميزة للمستخدم مشاهدة فيديو في نافذة صغيرة مثبتة في أحد أركان الشاشة أثناء التنقل بين التطبيقات أو تصفّح المحتوى على الشاشة الرئيسية.

في إطار العمل.

تستفيد ميزة "العرض على نافذة ضمن النافذة" من واجهات برمجة التطبيقات لميزة "وضع النوافذ المتعددة" المتوفّرة في الإصدار 7.0 من نظام التشغيل Android لتوفير نافذة التراكب المُثبَّتة للفيديو. لإضافة ميزة "نافذة ضمن النافذة" إلى تطبيقك، عليك تسجيل الأنشطة التي تتيح استخدام هذه الميزة وتبديل نشاطك إلى وضع "نافذة ضمن النافذة" عند الحاجة، و التأكّد من إخفاء عناصر واجهة المستخدم ومواصلة تشغيل الفيديو عندما يكون النشاط في وضع "نافذة ضمن النافذة".

تظهر نافذة "وضع الصورة في الصورة" في أعلى طبقة من الشاشة، في زاوية يختارها النظام.

تتوفّر ميزة "وضع الصورة في الصورة" أيضًا على أجهزة Android TV المتوافقة التي تعمل بالإصدار Android 14 (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث. على الرغم من تشابههما إلى حد كبير، هناك عوامل إضافية يجب مراعاتها عند استخدام وضع الصورة في الصورة على التلفزيون.

كيفية تفاعل المستخدمين مع نافذة "وضع الصورة في الصورة"

يمكن للمستخدمين سحب نافذة "وضع الصورة في الصورة" إلى موضع آخر. بدءًا من Android 12، يمكن للمستخدمين أيضًا:

  • انقر مرة واحدة على النافذة لعرض زر تبديل ملء الشاشة وزر إغلاق وزر إعدادات وإجراءات مخصّصة يوفّرها تطبيقك (مثل عناصر التحكّم في التشغيل).

  • انقر مرّتين على النافذة للتبديل بين حجم "نافذة ضمن النافذة" الحالي والحد الأقصى أو الحد الأدنى لحجم "نافذة ضمن النافذة". على سبيل المثال، يؤدي النقر مرّتين على نافذة مكبّرة إلى تصغيرها، والعكس صحيح أيضًا.

  • يمكنك إخفاء النافذة من خلال سحبها إلى الحافة اليمنى أو اليسرى. لإلغاء إخفاء النافذة، انقر على الجزء المرئي من النافذة المخفية أو اسحبها للخارج.

  • تغيير حجم نافذة "صورة في صورة" باستخدام ميزة التصغير/التكبير بإصبعَين

يتحكّم تطبيقك في وقت دخول النشاط الحالي إلى وضع "صورة في صورة". في ما يلي بعض الأمثلة:

  • يمكن أن يدخل النشاط في وضع "صورة في صورة" عندما ينقر المستخدم على زر الصفحة الرئيسية أو يمسح شاشة هاتفه للأعلى للوصول إلى الصفحة الرئيسية. بهذه الطريقة، يواصل تطبيق "خرائط Google" عرض الاتجاهات أثناء تنفيذ المستخدم لنشاط آخر في الوقت نفسه.

  • يمكن لتطبيقك نقل فيديو إلى وضع "نافذة ضمن النافذة" عندما يعود المستخدم من الفيديو لتصفّح محتوى آخر.

  • يمكن لتطبيقك تبديل الفيديو إلى وضع "نافذة ضمن النافذة" أثناء مشاهدة المستخدم لنهاية حلقة من المحتوى. تعرض الشاشة الرئيسية معلومات ترويجية أو موجزة عن الحلقة التالية في السلسلة.

  • يمكن أن يوفّر تطبيقك طريقة للمستخدمين لإضافة محتوى إضافي إلى قائمة المحتوى التالي أثناء مشاهدتهم لفيديو. يستمر تشغيل الفيديو في وضع "نافذة ضمن النافذة" بينما تعرض الشاشة الرئيسية نشاط اختيار محتوى.

الإفصاح عن توفّر ميزة "نافذة ضمن النافذة"

لا يتيح النظام تلقائيًا استخدام وضع "صورة في صورة" للتطبيقات. إذا أردت إتاحة ميزة "وضع الصورة في الصورة" في تطبيقك، سجِّل نشاط الفيديو في ملف البيان من خلال ضبط android:supportsPictureInPicture على true. حدِّد أيضًا أنّه علي نشاطك معالجة تغييرات إعدادات التنسيق لكي لا تتم его إعادة تشغيله عند حدوث تغييرات في التنسيق أثناء عمليات النقل إلى وضع "صورة في صورة".

<activity android:name="VideoActivity"
    android:supportsPictureInPicture="true"
    android:configChanges=
        "screenSize|smallestScreenSize|screenLayout|orientation"
    ...

التبديل إلى وضع "نافذة ضمن النافذة"

بدءًا من الإصدار 12 من نظام Android، يمكنك تبديل نشاطك إلى وضع "نافذة ضمن النافذة" من خلال ضبط العلامة setAutoEnterEnabled على true. باستخدام هذا الإعداد، يتم تبديل النشاط تلقائيًا إلى وضع "نافذة ضمن النافذة" حسب الحاجة بدون الحاجة إلى استدعاء enterPictureInPictureMode() بشكل صريح في onUserLeaveHint. ويعود ذلك بالفائدة المضافة المتمثّلة في توفير انتقالات أكثر سلاسة. لمعرفة التفاصيل، يُرجى الاطّلاع على مقالة جعل التحولات إلى وضع "صورة في صورة" أكثر سلاسة من خلال التنقّل بالإيماءات.

إذا كنت تستهدِف الإصدار 11 من نظام التشغيل Android أو الإصدارات الأقدم، يجب أن يطلب النشاط enterPictureInPictureMode() التبديل إلى وضع "صورة في صورة". على سبيل المثال، يبدِّل الرمز البرمجي التالي نشاطًا إلى وضع PiP عندما ينقر المستخدم على زر مخصّص في واجهة مستخدم التطبيق:

Kotlin

override fun onActionClicked(action: Action) {
    if (action.id.toInt() == R.id.lb_control_picture_in_picture) {
        activity?.enterPictureInPictureMode()
        return
    }
}

Java

@Override
public void onActionClicked(Action action) {
    if (action.getId() == R.id.lb_control_picture_in_picture) {
        getActivity().enterPictureInPictureMode();
        return;
    }
    ...
}

يمكنك تضمين منطق يتيح تبديل نشاط إلى وضع "صورة في صورة" بدلاً من الانتقال إلى الخلفية. على سبيل المثال، ينتقل تطبيق "خرائط Google" إلى وضع "نافذة ضمن النافذة" إذا ضغط المستخدم على زر الصفحة الرئيسية أو زر "التطبيقات المستخدَمة مؤخرًا" أثناء تنقّل التطبيق. يمكنك اكتشاف هذه الحالة من خلال إلغاء onUserLeaveHint():

Kotlin

override fun onUserLeaveHint() {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode()
    }
}

Java

@Override
public void onUserLeaveHint () {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode();
    }
}

يُنصح بتوفير تجربة انتقال سلسة للمستخدمين في وضع "صورة في صورة".

أضاف نظام التشغيل Android 12 تحسينات جمالية كبيرة على الانتقالات المتحرّكة بين النوافذ التي تعمل بالوضع الكامل ووضع "صورة في صورة". ننصحك بشدة بتنفيذ جميع التغييرات السارية، وبعد إجراء ذلك، يتم تلقائيًا توسيع نطاق هذه التغييرات ليشمل الشاشات الكبيرة، مثل الشاشات القابلة للطي والأجهزة اللوحية، بدون الحاجة إلى اتّخاذ أي إجراء آخر.

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

تشمل هذه التغييرات ما يلي:

  • تسهيل الانتقال إلى وضع "نافذة داخل النافذة" من خلال التنقّل بالإيماءات
  • ضبط sourceRectHint مناسب للدخول إلى وضع "نافذة داخل النافذة" والخروج منه
  • إيقاف ميزة تغيير حجم المحتوى غير الفيديوي بسلاسة

يمكنك الرجوع إلى Android مثال Kotlin PictureInPicture كمرجع لتفعيل تجربة انتقال مصقولة.

الانتقال إلى وضع "نافذة داخل النافذة" بسلاسة أكبر من خلال التنقّل بالإيماءات

اعتبارًا من نظام التشغيل Android 12، يوفّر الخيار setAutoEnterEnabled مؤثرًا حركيًا أكثر سلاسة للانتقال إلى محتوى الفيديو في وضع "نافذة ضمن النافذة" باستخدام التنقل باستخدام الإيماءات، على سبيل المثال، عند التمرير سريعًا للأعلى للانتقال إلى الشاشة الرئيسية من وضع ملء الشاشة.

أكمِل الخطوات التالية لإجراء هذا التغيير، واطّلِع على هذا النموذج للاطّلاع على مثال:

  1. استخدِم setAutoEnterEnabled لإنشاء PictureInPictureParams.Builder:

    Kotlin

    setPictureInPictureParams(PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build())

    Java

    setPictureInPictureParams(new PictureInPictureParams.Builder()
        .setAspectRatio(aspectRatio)
        .setSourceRectHint(sourceRectHint)
        .setAutoEnterEnabled(true)
        .build());
  2. يُرجى الاتصال بالرقم setPictureInPictureParams وإرسال PictureInPictureParams المعدَّل في وقت مبكر. لا ينتظر التطبيق استدعاء onUserLeaveHint (كما كان يحدث في Android 11).

    على سبيل المثال، قد تحتاج إلى استدعاء setPictureInPictureParams في التشغيل الأول وأي تشغيل لاحق إذا تم تغيير نسبة العرض إلى الارتفاع.

  3. يمكنك الاتصال برقم setAutoEnterEnabled(false)، ولكن عند الضرورة فقط. على سبيل المثال، قد لا تريد استخدام وضع "نافذة داخل النافذة" إذا كان التشغيل الحالي في حالة إيقاف مؤقت.

ضبط sourceRectHint مناسب للدخول إلى وضع "صورة في صورة" والخروج منه

منذ طرح ميزة "نافذة داخل النافذة" في Android 8.0، يشير الرمز setSourceRectHint إلى منطقة النشاط التي تظهر بعد الانتقال إلى وضع "نافذة داخل النافذة"، مثل حدود عرض الفيديو في مشغّل الفيديو.

في نظام التشغيل Android 12، يستخدم النظام sourceRectHint لتنفيذ مؤثرات متحركة أكثر سلاسة عند الدخول إلى وضع "نافذة ضمن النافذة" والخروج منه.

لضبط sourceRectHint بشكل صحيح للدخول إلى وضع "صورة في صورة" والخروج منه، اتّبِع الخطوات التالية:

  1. أنشئ PictureInPictureParams باستخدام الحدود المناسبة على النحو التالي: sourceRectHint. ننصحك أيضًا بإرفاق مراقب لتغيير التنسيق بمشغّل الفيديو:

    Kotlin

    val mOnLayoutChangeListener =
    OnLayoutChangeListener { v: View?, oldLeft: Int,
            oldTop: Int, oldRight: Int, oldBottom: Int, newLeft: Int, newTop:
            Int, newRight: Int, newBottom: Int ->
        val sourceRectHint = Rect()
        mYourVideoView.getGlobalVisibleRect(sourceRectHint)
        val builder = PictureInPictureParams.Builder()
            .setSourceRectHint(sourceRectHint)
        setPictureInPictureParams(builder.build())
    }
    
    mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener)

    Java

    private final View.OnLayoutChangeListener mOnLayoutChangeListener =
            (v, oldLeft, oldTop, oldRight, oldBottom, newLeft, newTop, newRight,
            newBottom) -> {
        final Rect sourceRectHint = new Rect();
        mYourVideoView.getGlobalVisibleRect(sourceRectHint);
        final PictureInPictureParams.Builder builder =
            new PictureInPictureParams.Builder()
                .setSourceRectHint(sourceRectHint);
        setPictureInPictureParams(builder.build());
    };
    
    mYourVideoView.addOnLayoutChangeListener(mOnLayoutChangeListener);
  2. إذا لزم الأمر، عدِّل sourceRectHint قبل أن يبدأ النظام مرحلة الخروج. عندما يكون النظام على وشك الخروج من وضع "صورة في صورة"، يتم وضع التدرّج الهرمي لملفه الشخصي في العرض وفقًا لإعدادات الوجهة (على سبيل المثال، ملء الشاشة). يمكن للتطبيق إرفاق مستمع لتغيير التنسيق بالعرض الجذر أو العرض المستهدَف (مثل عرض مشغّل الفيديو) لرصد الحدث وتعديل sourceRectHint قبل بدء الحركة.

    Kotlin

    // Listener is called immediately after the user exits PiP but before animating.
    playerView.addOnLayoutChangeListener { _, left, top, right, bottom,
                        oldLeft, oldTop, oldRight, oldBottom ->
        if (left != oldLeft
            || right != oldRight
            || top != oldTop
            || bottom != oldBottom) {
            // The playerView's bounds changed, update the source hint rect to
            // reflect its new bounds.
            val sourceRectHint = Rect()
            playerView.getGlobalVisibleRect(sourceRectHint)
            setPictureInPictureParams(
                PictureInPictureParams.Builder()
                    .setSourceRectHint(sourceRectHint)
                    .build()
            )
        }
    }

    Java

    // Listener is called right after the user exits PiP but before animating.
    playerView.addOnLayoutChangeListener((v, left, top, right, bottom,
                        oldLeft, oldTop, oldRight, oldBottom) -> {
        if (left != oldLeft
            || right != oldRight
            || top != oldTop
            || bottom != oldBottom) {
            // The playerView's bounds changed, update the source hint rect to
            // reflect its new bounds.
            final Rect sourceRectHint = new Rect();
            playerView.getGlobalVisibleRect(sourceRectHint);
            setPictureInPictureParams(
                new PictureInPictureParams.Builder()
                    .setSourceRectHint(sourceRectHint)
                    .build());
        }
    });

إيقاف ميزة تغيير حجم المحتوى غير الفيديوهات بسلاسة

يضيف نظام التشغيل Android 12 علامة setSeamlessResizeEnabled التي توفّر صورة متحركة ناتجة عن دمج الصور بشكلٍ سلس عند تغيير حجم المحتوى غير الفيديو في نافذة PiP. في السابق، كان تغيير حجم المحتوى غير الفيديو في "نافذة ضمن النافذة" قد يؤدي إلى ظهور عناصر مرئية مزعجة.

لإيقاف ميزة تغيير الحجم بسلاسة للمحتوى غير الفيديوهات، اتّبِع الخطوات التالية:

Kotlin

setPictureInPictureParams(PictureInPictureParams.Builder()
    .setSeamlessResizeEnabled(false)
    .build())

Java

setPictureInPictureParams(new PictureInPictureParams.Builder()
    .setSeamlessResizeEnabled(false)
    .build());

التعامل مع واجهة المستخدم أثناء وضع "نافذة ضمن النافذة"

عندما يدخل النشاط وضع "نافذة ضمن النافذة" أو يخرج منه، يُطلِق النظام Activity.onPictureInPictureModeChanged() أو Fragment.onPictureInPictureModeChanged().

يقدّم Android 15 تغييرات تضمن عملية انتقال أكثر سلاسة عند الدخول إلى وضع "نافذة داخل النافذة". ويُعدّ ذلك مفيدًا للتطبيقات التي تتضمّن عناصر واجهة مستخدم متراكبة على واجهة المستخدم الرئيسية، والتي تنتقل إلى وضع "صورة في صورة".

يستخدم المطوّرون دالة الاستدعاء onPictureInPictureModeChanged() لتحديد المنطق الذي يغيّر مستوى ظهور عناصر واجهة المستخدم التي تمّت تغطيتها. يتم تشغيل ردّ الاتصال هذا عند اكتمال الحركة المخصّصة للدخول إلى وضع "صورة في صورة" أو الخروج منه. بدءًا من Android 15، تتضمّن فئة PictureInPictureUiState حالة جديدة.

باستخدام حالة واجهة المستخدم الجديدة هذه، ترصد التطبيقات التي تستهدف الإصدار 15 من Android Activity#onPictureInPictureUiStateChanged() مكالمة الاستدعاء التي يتمّ استدعاؤها باستخدام isTransitioningToPip() فور بدء الصورة المصغّرة للنافذة المنبثقة. هناك العديد من عناصر واجهة المستخدم التي لا تكون ملائمة للتطبيق عندما يكون في وضع "صورة في صورة"، مثل طرق العرض أو التنسيقات التي تتضمّن معلومات مثل الاقتراحات والفيديوهات القادمة والتقييمات والعناوين. عندما ينتقل التطبيق إلى وضع "نافذة ضمن النافذة"، استخدِم دالة الاستدعاء onPictureInPictureUiStateChanged() لإخفاء عناصر واجهة المستخدم هذه. عندما ينتقل التطبيق إلى وضع ملء الشاشة من نافذة "نافذة ضمن نافذة"، استخدِم دالة الاستدعاء onPictureInPictureModeChanged() لإلغاء إخفاء هذه العناصر، كما هو موضّح في الأمثلة التالية:

Kotlin

override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) {
        if (pipState.isTransitioningToPip()) {
          // Hide UI elements.
        }
    }

Java

@Override
public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {
        if (pipState.isTransitioningToPip()) {
          // Hide UI elements.
        }
    }

Kotlin

override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
        if (isInPictureInPictureMode) {
          // Unhide UI elements.
        }
    }

Java

@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
        if (isInPictureInPictureMode) {
          // Unhide UI elements.
        }
    }

يساعد هذا التبديل السريع لعناصر واجهة المستخدم غير ذات الصلة (لتشغيل نافذة PiP) في ضمان ظهور صورة متحركة سلسة وبدون وميض عند تشغيل وضع PiP.

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

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

إضافة عناصر تحكّم

يمكن أن تعرض نافذة "وضع الصورة في الصورة" عناصر التحكّم عندما يفتح المستخدم قائمة النافذة (من خلال النقر على النافذة على جهاز جوّال أو اختيار القائمة من جهاز التحكّم عن بُعد بالتلفزيون).

إذا كان التطبيق يتضمّن جلسة وسائط قيد التشغيل، ستظهر عناصر التحكّم في التشغيل والإيقاف المؤقت والتقديم والترجيع.

يمكنك أيضًا تحديد الإجراءات المخصّصة بشكل صريح من خلال إنشاء PictureInPictureParams مع PictureInPictureParams.Builder.setActions() قبل الدخول إلى وضع "نافذة داخل النافذة"، وضبط المَعلمات عند الدخول إلى وضع "نافذة داخل النافذة" باستخدام enterPictureInPictureMode(android.app.PictureInPictureParams) أو setPictureInPictureParams(android.app.PictureInPictureParams). يُرجى توخّي الحذر. إذا حاولت إضافة أكثر من getMaxNumPictureInPictureActions()، لن تحصل إلا على الحد الأقصى المسموح به.

مواصلة تشغيل الفيديو أثناء استخدام ميزة "صورة في صورة"

عند تبديل نشاطك إلى وضع "صورة في صورة"، يضع النظام النشاط في حالة التوقف مؤقتًا ويُطلِب منه onPause(). يجب عدم إيقاف تشغيل الفيديو مؤقتًا، بل مواصلة تشغيله إذا تم إيقاف النشاط مؤقتًا أثناء الانتقال إلى وضع "نافذة ضمن النافذة".

في نظام التشغيل Android 7.0 والإصدارات الأحدث، يجب إيقاف تشغيل الفيديو مؤقتًا واستئنافه عندما يطلب النظام رمزَي onStop() و onStart() لنشاطك. من خلال إجراء ذلك، يمكنك تجنُّب التحقّق مما إذا كان تطبيقك في وضع "صورة في صورة" في onPause() ومواصلة التشغيل صراحةً.

إذا لم تضبط العلامة setAutoEnterEnabled على true وكنت بحاجة إلى إيقاف التشغيل مؤقتًا في عملية تنفيذ onPause()، تحقّق من وضع "صورة في صورة" من خلال استدعاء isInPictureInPictureMode() وعالج التشغيل بشكلٍ مناسب. مثلاً:

Kotlin

override fun onPause() {
    super.onPause()
    // If called while in PiP mode, do not pause playback.
    if (isInPictureInPictureMode) {
        // Continue playback.
    } else {
        // Use existing playback logic for paused activity behavior.
    }
}

Java

@Override
public void onPause() {
    // If called while in PiP mode, do not pause playback.
    if (isInPictureInPictureMode()) {
        // Continue playback.
        ...
    } else {
        // Use existing playback logic for paused activity behavior.
        ...
    }
}

عندما ينتقل نشاطك من وضع "نافذة داخل النافذة" إلى وضع ملء الشاشة، يُستأنف النظام نشاطك ويُطلِب من الأسلوب onResume() تنفيذ مهامه.

استخدام نشاط تشغيل واحد لميزة "صورة في صورة"

في تطبيقك، قد يختار المستخدم فيديو جديدًا عند تصفّح المحتوى على الشاشة الرئيسية، بينما يكون نشاط تشغيل الفيديو في وضع "نافذة ضمن النافذة". تشغيل الفيديو الجديد في نشاط التشغيل الحالي في وضع ملء الشاشة، بدلاً من بدء نشاط جديد قد يربّك المستخدم

لضمان استخدام نشاط واحد لطلبات تشغيل الفيديو وبدء وضع "نافذة ضمن النافذة" أو إيقافه حسب الحاجة، اضبط android:launchMode للنشاط على singleTask في البيان:

<activity android:name="VideoActivity"
    ...
    android:supportsPictureInPicture="true"
    android:launchMode="singleTask"
    ...

في نشاطك، يمكنك إلغاء القيمة المُدخلة في onNewIntent() ومعالجة الفيديو الجديد، مع إيقاف تشغيل أي فيديو حالي إذا لزم الأمر.

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

قد تكون ميزة "صورة في صورة" غير مفعّلة على الأجهزة التي تتضمّن ذاكرة وصول عشوائي (RAM) منخفضة. قبل أن يستخدم تطبيقك ميزة "وضع الصورة في الصورة"، تحقّق من توفّرها من خلال الاتصال بالرقم hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE).

تهدف ميزة "نافذة ضِمن نافذة" إلى الأنشطة التي تشغّل الفيديوهات بملء الشاشة. عند تبديل نشاطك إلى وضع "نافذة ضمن النافذة"، تجنَّب عرض أي شيء باستثناء محتوى الفيديو. تتبُّع الحالات التي يدخل فيها نشاطك إلى وضع "صورة في صورة" وإخفاء عناصر واجهة المستخدم، كما هو موضّح في مقالة التعامل مع واجهة المستخدم أثناء استخدام وضع "صورة في صورة"

عندما يكون النشاط في وضع "صورة في صورة"، لا يتم تلقائيًا التركيز على الإدخال. لتلقّي أحداث الإدخال أثناء استخدام وضع "نافذة داخل النافذة"، استخدِم MediaSession.setCallback(). لمزيد من المعلومات عن استخدام setCallback()، يُرجى الاطّلاع على مقالة عرض بطاقة "المحتوى الذي يتم تشغيله الآن".

عندما يكون تطبيقك في وضع "نافذة ضمن نافذة"، يمكن أن يؤدي تشغيل الفيديو في نافذة "نافذة ضمن نافذة" إلى تداخل الصوت مع تطبيق آخر، مثل تطبيق مشغّل موسيقى أو تطبيق بحث صوتي. لتجنُّب ذلك، يمكنك طلب تركيز الصوت عند بدء تشغيل الفيديو ومعالجة إعلامات تغيير تركيز الصوت، كما هو موضّح في مقالة إدارة تركيز الصوت. إذا تلقّيت إشعارًا بشأن فقدان تركيز الصوت في وضع "صورة في صورة"، أوقِف تشغيل الفيديو مؤقتًا أو أوقِفه.

عندما يكون تطبيقك على وشك الدخول إلى وضع "نافذة ضمن النافذة"، يُرجى العِلم أنّه لن يتم سوى عرض النشاط الرئيسي في وضع "نافذة ضمن النافذة". في بعض الحالات، مثل الأجهزة التي تتيح استخدام عدّة نوافذ، قد يظهر النشاط أدناه الآن ويصبح مرئيًا مرة أخرى بجانب نشاط وضع الصورة في الصورة. يجب معالجة هذا الطلب وفقًا لذلك، بما في ذلك النشاط أدناه الذي يتلقّى مكالمة onResume() أو onPause(). ومن المُحتمَل أيضًا أن يتفاعل المستخدم مع النشاط. على سبيل المثال، إذا كان لديك نشاط قائمة فيديو معروض ونشاط تشغيل فيديو في وضع "صورة في صورة"، قد يختار المستخدم فيديو جديدًا من القائمة ومن المفترض أن يتم تعديل نشاط "صورة في صورة" وفقًا لذلك.

نموذج رمز إضافي

لتنزيل نموذج تطبيق مكتوب بلغة Kotlin، اطّلِع على نموذج تطبيق Android PictureInPicture (Kotlin).