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

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

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

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

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

تتوفّر ميزة "نافذة ضمن النافذة" أيضًا على أجهزة Android TV OS المتوافقة التي تعمل بالإصدار 14 من نظام التشغيل Android (المستوى 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() للتبديل إلى وضع "نافذة ضمن النافذة". على سبيل المثال، يغيّر الرمز التالي حالة النشاط إلى وضع &quot;نافذة ضمن النافذة&quot; عندما ينقر المستخدم على زر مخصّص في واجهة مستخدم التطبيق:

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

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

Kotlin

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

Java

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

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

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

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

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

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

يمكنك الرجوع إلى نموذج PictureInPicture في Kotlin على 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) عند الضرورة فقط. على سبيل المثال، من المحتمل أنّك لا تريد الانتقال إلى وضع &quot;نافذة داخل النافذة&quot; إذا كان التشغيل الحالي متوقفًا مؤقتًا.

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

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

Kotlin

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

Java

setPictureInPictureParams(new PictureInPictureParams.Builder()
    .setSeamlessResizeEnabled(true)
    .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.
        }
    }

يساعد مفتاح التبديل هذا الذي يتيح إخفاء عناصر واجهة المستخدم غير ذات الصلة بسرعة (في نافذة "صورة داخل صورة") في ضمان عرض سلس وخالٍ من الوميض لرسوم متحركة عند فتح نافذة "صورة داخل صورة".

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

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

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

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

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

يمكنك أيضًا تحديد إجراءات مخصّصة بشكل صريح من خلال إنشاء 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).

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

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

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

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

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

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