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

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

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

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

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

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

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

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

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

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

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

  • غيِّر حجم النافذة في وضع "نافذة ضمن النافذة" باستخدام التصغير أو التكبير بإصبعين.

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

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

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

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

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

الإعلان عن توفّر ميزة "نافذة ضمن النافذة" (PIP)

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

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

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

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

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

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();
    }
}

إجراء ننصح به: تقديم تجربة محسّنة لنقل البيانات في ميزة "نافذة ضمن النافذة" (PIP)

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

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

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

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

راجِع نموذج Kotlin PictureInPicture على نظام التشغيل Android كمرجع لتفعيل تجربة انتقال منسَّقة.

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

اعتبارًا من نظام التشغيل 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 التي تقدّم صورة متحركة أكثر سلاسة عند تغيير حجم المحتوى غير المرئي في نافذة "نافذة ضمن النافذة". في السابق، كان تغيير حجم المحتوى غير الفيديو في "نافذة ضمن النافذة" قد يؤدي إلى ظهور عناصر مرئية مزعجة.

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

Kotlin

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

Java

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

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

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

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

يستخدم المطوّرون دالة رد الاتصال onPictureInPictureModeChanged() لتحديد المنطق الذي يبدِّل إمكانية ظهور عناصر واجهة المستخدم المركّبة. يتم تشغيل معاودة الاتصال هذه عند اكتمال الحركة للدخول أو الخروج في وضع "نافذة ضمن النافذة". بدءًا من الإصدار 15 من نظام Android، ستشمل الفئة 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.
        }
    }

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

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

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

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

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

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

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

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

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

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

إذا لم تكن علامة setAutoEnterEnabled مضبوطة على true وكنت بحاجة إلى إيقاف التشغيل مؤقتًا أثناء التنفيذ في onPause()، تحقَّق من وضع "نافذة ضمن النافذة" (PIP) من خلال الاتصال بالرقم 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 Sample (Kotlin).