تضمين الأنشطة

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

الشكل 1. تطبيق "الإعدادات" مع الأنشطة بجانب بعضها

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

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

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

تتيح ميزة "تضمين الأنشطة" تغيير اتجاه الجهاز، وتعمل بسلاسة على الأجهزة القابلة للطي، كما تتيح تجميع الأنشطة وتفكيكها أثناء طي الجهاز وفتحه.

تتوفّر ميزة تضمين الأنشطة على معظم الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار 12L من نظام التشغيل Android (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأحدث.

نافذة المهمة المُقسّمة

تؤدي عملية تضمين النشاط إلى تقسيم نافذة مهمة التطبيق إلى حاويتَين: حاوية أساسية و حاوية ثانوية. تحتوي الحاويات على الأنشطة التي يتم إطلاقها من النشاط الرئيسي أو من الأنشطة الأخرى المتوفّرة في الحاويات.

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

تتيح لك ميزة "تضمين الأنشطة" عرض الأنشطة بعدة طرق. يمكن لتطبيقك تقسيم نافذة المهام من خلال تشغيل نشاطَين جنبًا إلى جنب في الوقت نفسه:

الشكل 2. نشاطان جنبًا إلى جنب

أو يمكن أن يؤدي النشاط الذي يشغل نافذة المهام بأكملها إلى إنشاء تقسيم من خلال بدء نشاط جديد بجانب:

الشكل 3. يبدأ النشاط "أ" النشاط "ب" على الجانب.

يمكن للأنشطة التي تم تقسيمها ومشاركة نافذة مهمة فيها تشغيل أنشطة أخرى بالطرق التالية:

  • على الجانب أعلى نشاط آخر:

    الشكل 4. يبدأ النشاط (أ) النشاط (ج) على الجانب الأيمن من النشاط (ب).
  • إلى الجانب، وحرِّك القسم إلى الجانب، ما يؤدي إلى إخفاء النشاط الأساسي السابق:

    الشكل 5. يبدأ النشاط "ب" النشاط "ج" على الجانب وينقل القسم المُقسَّم بشكل جانبي.
  • ابدأ نشاطًا في مكانه في أعلى الصفحة، أي في مجموعة الأنشطة نفسها:

    الشكل 6. يبدأ النشاط "ب" النشاط "ج" بدون أي علامات إضافية للنوايا.
  • افتح نافذة نشاط كاملة في المهمة نفسها:

    الشكل 7. يبدأ النشاط "أ" أو النشاط "ب" النشاط "ج" الذي يملأ نافذة المهام.

الانتقال إلى الصفحة السابقة

يمكن أن تتضمّن الأنواع المختلفة من التطبيقات قواعد مختلفة للتنقّل للخلف في حالة نافذة مهمة مُقسّمة استنادًا إلى التبعيات بين الأنشطة أو كيفية تنشيط المستخدِمين لحدث الرجوع، على سبيل المثال:

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

يتم إرسال حدث الرجوع إلى آخر نشاط تم التركيز عليه عند استخدام زر التنقّل.

للتنقّل بالاستناد إلى الإيماءات:

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

  • الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات) والإصدارات الأحدث

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

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

تنسيق متعدد الأقسام

تتيح لك مكتبة Jetpack WindowManager إنشاء نشاط يضم تنسيقًا متعدّد الأقسام على الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار 12L من نظام التشغيل Android (المستوى 32 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث وعلى بعض الأجهزة التي تعمل بإصدارات أقدم من نظام التشغيل. يمكن للتطبيقات الحالية التي تستند إلى أنشطة متعدّدة بدلاً من الأجزاء أو التنسيقات المستندة إلى العرض، مثل SlidingPaneLayout، أن تقدّم تجربة محسّنة للمستخدم على الشاشة الكبيرة بدون إعادة صياغة رمز المصدر.

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

الشكل 8. تم بدء نشاطَين في الوقت نفسه بتنسيق شاشة متعددة الأقسام.

تقسيم السمات

يمكنك تحديد نسبة توزيع مساحة نافذة المهام بين الحاويات المُقسَّمة وكيفية ترتيب الحاويات بالنسبة إلى بعضها.

بالنسبة إلى القواعد المحدّدة في ملف إعداد XML، اضبط السمات التالية:

  • splitRatio: لضبط نسب الحاوية القيمة هي عدد دقائق عائم في الفاصل المفتوح (0.0, 1.0).
  • splitLayoutDirection: تحدّد هذه السمة كيفية ترتيب الحاويات المُقسّمة بالنسبة إلى بعضها. تشمل القيم ما يلي:
    • ltr: من اليسار إلى اليمين
    • rtl: من اليمين إلى اليسار
    • locale: يتم تحديد ltr أو rtl من إعدادات اللغة

راجِع قسم إعدادات XML للاطّلاع على أمثلة.

بالنسبة إلى القواعد التي تم إنشاؤها باستخدام واجهات برمجة تطبيقات WindowManager، أنشئ عنصر SplitAttributes باستخدام SplitAttributes.Builder واستخدِم متدّجات الإنشاء التالية:

  • setSplitType(): لضبط نسب الحاويات المُقسَّمة. راجِع ملف تعريف الارتباط SplitAttributes.SplitType للاطّلاع على الوسيطات الصالحة، بما في ذلك ملف تعريف الارتباط SplitAttributes.SplitType.ratio().
  • setLayoutDirection(): لضبط تنسيق الحاويات راجِع SplitAttributes.LayoutDirection للاطّلاع على القيم المحتمَلة.

اطّلِع على قسم WindowManager API للاطّلاع على أمثلة.

الشكل 9. قسمان من النشاط معروضان من اليمين إلى اليسار ولكن بنسبة تقسيم مختلفة

العناصر النائبة

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

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

الشكل 10. جهاز قابل للطي يُطوى ويُفتح يتم الانتهاء من نشاط العنصر النائب وإعادة إنشائه عند تغيير حجم الشاشة.

ومع ذلك، يمكن أن تلغي سمة stickyPlaceholder لطريقة SplitPlaceholderRule أو setSticky() في SplitPlaceholder.Builder السلوك التلقائي. عندما تحدّد السمة أو الطريقة قيمة true، يعرض النظام العنصر النائب كأهم نشاط في نافذة المهام عند تغيير حجم الشاشة إلى شاشة لوحة واحدة من شاشة لوحتَين (راجِع إعدادات التقسيم للحصول على مثال).

الشكل 11. جهاز قابل للطيّ يتم طيّه وفتحه العنصر النائب النشاط ثابت.

تغييرات حجم النافذة

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

لا يتم عرض أنشطة العناصر النائبة إلا عند توفّر عرض كافٍ للشاشة من أجل القسمة. على الشاشات الأصغر حجمًا، يتم إغلاق العنصر النائب تلقائيًا. عندما تصبح مساحة العرض كبيرة بما يكفي مرة أخرى، تتم إعادة إنشاء العنصر النائب. (اطّلِع على القسم العناصر النائبة).

يمكن تجميع الأنشطة لأنّ WindowManager يُرتِّب الأنشطة في اللوحة الثانوية فوق الأنشطة في اللوحة الأساسية بترتيب أبجدي عكسي.

أنشطة متعددة في اللوحة الثانوية

يبدأ النشاط "ب" النشاط "ج" بدون أي علامات إضافية للنوايا:

تقسيم النشاط الذي يحتوي على الأنشطة "أ" و"ب" و"ج" مع تجميع "ج" فوق
          "ب"

ما أدّى إلى ترتيب الأنشطة التالي في المهمة نفسها:

مجموعة أنشطة ثانوية تحتوي على النشاط "ج" فوق النشاط "ب"
          يتم تجميع الحزمة الثانوية فوق حزمة النشاط الأساسي
          التي تحتوي على النشاط "أ".

وبالتالي، في نافذة مهام أصغر حجمًا، يتم تصغير التطبيق إلى نشاط واحد مع C في أعلى الحزمة:

نافذة صغيرة تعرض النشاط "ج" فقط

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

في حال استعادة إعدادات نافذة المهام إلى حجم أكبر يمكنه استيعاب عدة أقسام، يتم عرض الأنشطة جنبًا إلى جنب مرة أخرى.

التقسيمات المكدّسة

يبدأ النشاط "ب" النشاط "ج" على الجانب وينقل الجزء المُقسَّم إلى الجانب:

نافذة المهام تعرِض النشاطَين "أ" و"ب"، ثم النشاطَين "ب" و"ج".

والنتيجة هي الترتيب z التالي للأنشطة في المهمة نفسها:

الأنشطة "أ" و"ب" و"ج" في حزمة واحدة يتم تجميع الأنشطة
          بالترتيب التالي من الأعلى إلى الأسفل: ج، ب، أ.

في نافذة مهام أصغر حجمًا، يتم تصغير التطبيق إلى نشاط واحد مع C فيверху:

نافذة صغيرة تعرض النشاط "ج" فقط

الاتجاه العمودي الثابت

يتيح إعداد البيان android:screenOrientation للتطبيقات تقييد الأنشطة بالاتجاه العمودي أو الأفقي. لتحسين تجربة المستخدم على الأجهزة ذات الشاشات الكبيرة، مثل الأجهزة اللوحية والأجهزة القابلة للطي، يمكن لمصنعي الأجهزة (OEM) تجاهل طلبات اتجاه الشاشة وعرض التطبيق في وضع شاشة عريضة عمودية على الشاشات الأفقية أو وضع شاشة عريضة أفقية على الشاشات الرأسية.

الشكل 12. الأنشطة المُعدّة للعرض على شاشة عريضة أفقيًا: وضع ثابت في الوضع العمودي على جهاز في الوضع الأفقي (على يمين الشاشة)، ووضع ثابت في الوضع الأفقي على جهاز في الوضع العمودي (على يسار الشاشة).

وبالمثل، عند تفعيل ميزة تضمين الأنشطة، يمكن لمصنّعي المعدّات الأصلية تخصيص الأجهزة لعرض أنشطة في وضع مُعدّ للعرض على شاشة عريضة في الوضع العمودي على الشاشات الكبيرة (يجب أن يكون العرض أكبر من أو يساوي 600dp). عندما يشغِّل نشاط في الوضع العمودي الثابت نشاطًا ثانيًا، يمكن للجهاز عرض النشاطَين جنبًا إلى جنب في شاشة من قسمَين.

الشكل 13. يبدأ النشاط "أ" في الوضع العمودي الثابت النشاط "ب" على الجانب.

أضِف دائمًا السمة android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED إلى ملف بيان تطبيقك لإعلام الأجهزة بأنّ تطبيقك يتيح ميزة ملفّات النشاط المضمّنة (راجِع القسم إعداد "التقسيم" ). يمكن للأجهزة المخصّصة من المصنّعين الأصليين للأجهزة بعد ذلك تحديد ما إذا كان سيتم عرض الأنشطة في وضع "شاشة عريضة أفقيًا" أو في وضع "شاشة عريضة عموديًا".

إعدادات التقسيم

تعمل قواعد التقسيم على ضبط عمليات تقسيم الأنشطة. يمكنك تحديد قواعد التقسيم في ملف إعدادات XML أو من خلال إجراء طلبات WindowManager API في Jetpack.

وفي كلتا الحالتَين، يجب أن يصل تطبيقك إلى مكتبة WindowManager وأن يُعلم النظام بأنّ التطبيق قد نفَّذ عملية تضمين النشاط.

اتّبِع الخطوات التالية:

  1. أضِف أحدث مكتبة WindowManager التي يعتمد عليها تطبيقك إلى ملف build.gradle على مستوى الوحدة في تطبيقك، على سبيل المثال:

    implementation 'androidx.window:window:1.1.0-beta02'

    توفّر مكتبة WindowManager جميع المكوّنات المطلوبة لدمج النشاط.

  2. أطلِع النظام على أنّ تطبيقك قد نفَّذ عملية تضمين النشاط.

    أضِف السمة android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED إلى العنصر <application> في ملف بيان التطبيق، واضبط القيمة على true، على سبيل المثال:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
        <application>
            <property
                android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
                android:value="true" />
        </application>
    </manifest>
    

    في الإصدار 1.1.0-alpha06 من WindowManager والإصدارات الأحدث، يتم إيقاف عمليات تقسيم تضمين النشاط ما لم تتم إضافة السمة إلى البيان وضبطها على true.

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

إعدادات XML

لإنشاء عملية تضمين نشاط مستندة إلى XML، أكمِل الخطوات التالية:

  1. أنشئ ملف موارد XML يؤدي ما يلي:

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

    مثلاً:

    <!-- main_split_config.xml -->
    
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Define a split for the named activities. -->
        <SplitPairRule
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:finishPrimaryWithSecondary="never"
            window:finishSecondaryWithPrimary="always"
            window:clearTop="false">
            <SplitPairFilter
                window:primaryActivityName=".ListActivity"
                window:secondaryActivityName=".DetailActivity"/>
        </SplitPairRule>
    
        <!-- Specify a placeholder for the secondary container when content is
             not available. -->
        <SplitPlaceholderRule
            window:placeholderActivityName=".PlaceholderActivity"
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:stickyPlaceholder="false">
            <ActivityFilter
                window:activityName=".ListActivity"/>
        </SplitPlaceholderRule>
    
        <!-- Define activities that should never be part of a split. Note: Takes
             precedence over other split rules for the activity named in the
             rule. -->
        <ActivityRule
            window:alwaysExpand="true">
            <ActivityFilter
                window:activityName=".ExpandedActivity"/>
        </ActivityRule>
    
    </resources>
    
  2. أنشئ عنصرًا تمهيديًا.

    يحلّل مكوّن WindowManager RuleController ملف الإعدادات بتنسيق XML ويُتيح للنظام القواعد. توفّر مكتبة بدء التشغيل في Jetpack Initializer ملف XML لأجل RuleController عند بدء تشغيل التطبيق حتى تكون القواعد سارية عند بدء أي أنشطة.

    لإنشاء عنصر إعداد، اتّبِع الخطوات التالية:

    1. أضِف أحدث مكتبة لاعتماد Jetpack Startup إلى ملف build.gradle على مستوى الوحدة، على سبيل المثال:

      implementation 'androidx.startup:startup-runtime:1.1.1'

    2. أنشئ فئة تنفّذ واجهة Initializer.

      توفّر أداة الإعداد قواعد التقسيم RuleController من خلال تمرير رقم تعريف ملف الإعداد بتنسيق XML (main_split_config.xml) إلى طريقة RuleController.parseRules().

      Kotlin

      class SplitInitializer : Initializer<RuleController> {
      
          override fun create(context: Context): RuleController {
              return RuleController.getInstance(context).apply {
                  setRules(RuleController.parseRules(context, R.xml.main_split_config))
              }
          }
      
          override fun dependencies(): List<Class<out Initializer<*>>> {
              return emptyList()
          }
      }

      Java

      public class SplitInitializer implements Initializer<RuleController> {
      
           @NonNull
           @Override
           public RuleController create(@NonNull Context context) {
               RuleController ruleController = RuleController.getInstance(context);
               ruleController.setRules(
                   RuleController.parseRules(context, R.xml.main_split_config)
               );
               return ruleController;
           }
      
           @NonNull
           @Override
           public List<Class<? extends Initializer<?>>> dependencies() {
               return Collections.emptyList();
           }
      }
  3. أنشئ مقدّم محتوى لتعريفات القواعد.

    أضِف androidx.startup.InitializationProvider إلى ملف بيان تطبيقك بصفته <provider>. أدرِج إشارة إلى تنفيذ RuleController SplitInitializer:

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- Make SplitInitializer discoverable by InitializationProvider. -->
        <meta-data android:name="${applicationId}.SplitInitializer"
            android:value="androidx.startup" />
    </provider>
    

    يرصد InitializationProvider SplitInitializer ويُنشئه قبل استدعاء طريقة onCreate() في التطبيق. نتيجةً لذلك، يتم تطبيق قواعد التقسيم عند بدء النشاط الرئيسي للتطبيق.

واجهة برمجة التطبيقات WindowManager

يمكنك تنفيذ عملية تضمين الأنشطة آليًا باستخدام عدد من طلبات برمجة التطبيقات. يمكنك إجراء المكالمات في طريقة onCreate() لفئة فرعية من Application لضمان سريان القواعد قبل بدء أي أنشطة.

لإنشاء تقسيم نشاط آليًا، اتّبِع الخطوات التالية:

  1. لإنشاء قاعدة تقسيم:

    1. أنشئ SplitPairFilter يحدِّد الأنشطة التي تشترك في التقسيم:

      Kotlin

      val splitPairFilter = SplitPairFilter(
         ComponentName(this, ListActivity::class.java),
         ComponentName(this, DetailActivity::class.java),
         null
      )

      Java

      SplitPairFilter splitPairFilter = new SplitPairFilter(
         new ComponentName(this, ListActivity.class),
         new ComponentName(this, DetailActivity.class),
         null
      );
    2. أضِف الفلتر إلى مجموعة فلاتر:

      Kotlin

      val filterSet = setOf(splitPairFilter)

      Java

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
    3. أنشئ سمات التنسيق للتقسيم:

      Kotlin

      val splitAttributes: SplitAttributes = SplitAttributes.Builder()
          .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
          .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
          .build()

      Java

      final SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();

      تنشئ SplitAttributes.Builder عنصرًا يحتوي على سمات تخطيط:

      • setSplitType(): يحدِّد كيفية تخصيص مساحة العرض المتاحة لكل حاوية نشاط. يحدِّد نوع تقسيم النسبة نسبة مساحة العرض المتاحة المخصّصة للعلبة الأساسية، وتشغل العلبة الثانوية الجزء المتبقّي من مساحة العرض المتاحة.
      • setLayoutDirection(): لتحديد كيفية تنسيق حاويات الأنشطة بالنسبة إلى بعضها، الحاوية الأساسية أولاً.
    4. أنشئ SplitPairRule:

      Kotlin

      val splitPairRule = SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build()

      Java

      SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build();

      SplitPairRule.Builder لإنشاء القاعدة وضبطها:

      • filterSet: يحتوي على فلاتر أزواج التقسيم التي تحدّد حالات تطبيق القاعدة من خلال تحديد الأنشطة التي تتشارك تقسيمًا.
      • setDefaultSplitAttributes(): تُطبِّق سمات التنسيق على القاعدة.
      • setMinWidthDp(): لضبط الحد الأدنى لعرض الشاشة (بوحدات بكسل لا تعتمد على الكثافة (dp)) الذي يتيح التقسيم.
      • setMinSmallestWidthDp(): لضبط الحد الأدنى للقيمة (بالوحدة dp) التي يجب أن يمتلكها أصغر سمة من سمتَي الشاشة لتفعيل القسمة بغض النظر عن اتجاه الجهاز
      • setMaxAspectRatioInPortrait(): لضبط الحد الأقصى لنسبة قياس الشاشة (الارتفاع إلى العرض) في الوضع العمودي الذي يتم عرض تقسيمات النشاط عليه. إذا كانت نسبة العرض إلى الارتفاع لشاشة في الوضع العمودي تتعدى الحد الأقصى لنسبة العرض إلى الارتفاع، يتم إيقاف عمليات التقسيم بغض النظر عن عرض الشاشة. ملاحظة: القيمة التلقائية هي 1.4، ما يؤدي إلى أن تشغل الأنشطة نافذة المهام بالكامل في الوضع عمودي على معظم الأجهزة اللوحية. يمكنك أيضًا الاطّلاع على SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT و setMaxAspectRatioInLandscape(). القيمة التلقائية لاتجاه اللوحة هي ALWAYS_ALLOW.
      • setFinishPrimaryWithSecondary(): لضبط كيفية تأثير إنهاء كل الأنشطة في الحاوية الثانوية في الأنشطة في الحاوية الأساسية. يشير الرمز NEVER إلى أنّه يجب ألا يُنهي النظام الأنشطة الأساسية عند انتهاء جميع الأنشطة في الحاوية الثانوية (راجِع إنهاء الأنشطة).
      • setFinishSecondaryWithPrimary(): لضبط كيفية تأثير إنهاء كل الأنشطة في الحاوية الأساسية في الأنشطة في الحاوية الثانوية. يشير الرمز ALWAYS إلى أنّه على النظام دائمًا إنهاء الأنشطة في الحاوية الثانوية عند انتهاء جميع الأنشطة في الحاوية الأساسية (راجِع إنهاء الأنشطة).
      • setClearTop(): يحدِّد ما إذا كان سيتم إنهاء جميع الأنشطة في الحاوية الثانوية عند بدء نشاط جديد في الحاوية. تُحدِّد القيمة false أنّ الأنشطة الجديدة يتم تجميعها فوق الأنشطة المتوفّرة حاليًا في الحاوية الثانوية.
    5. احصل على مثيل فريد من WindowManager RuleController، وأضِف القاعدة:

      Kotlin

        val ruleController = RuleController.getInstance(this)
        ruleController.addRule(splitPairRule)
        

      Java

        RuleController ruleController = RuleController.getInstance(this);
        ruleController.addRule(splitPairRule);
        
  2. أنشئ عنصر نائب للحاوية الثانوية عندما لا يتوفّر محتوى:

    1. أنشئ ActivityFilter يحدِّد النشاط الذي يشارك العنصر النائب معه مشاركة في تقسيم نافذة المهام:

      Kotlin

      val placeholderActivityFilter = ActivityFilter(
          ComponentName(this, ListActivity::class.java),
          null
      )

      Java

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );
    2. أضِف الفلتر إلى مجموعة فلاتر:

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

      Java

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
    3. أنشئ SplitPlaceholderRule:

      Kotlin

      val splitPlaceholderRule = SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            Intent(context, PlaceholderActivity::class.java)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build()

      Java

      SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            new Intent(context, PlaceholderActivity.class)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build();

      SplitPlaceholderRule.Builder لإنشاء القاعدة وضبطها:

      • placeholderActivityFilterSet: يحتوي على فلاتر الأنشطة التي تحدد وقت تطبيق القاعدة من خلال تحديد الأنشطة المرتبطة بالنشاط النائب.
      • Intent: لتحديد إطلاق نشاط العنصر النائب.
      • setDefaultSplitAttributes(): تُطبِّق سمات التنسيق على القاعدة.
      • setMinWidthDp(): يضبط الحد الأدنى لعرض الشاشة (بوحدات بكسل مستقلة الكثافة، dp) الذي يسمح بتقسيم الشاشة.
      • setMinSmallestWidthDp(): تُستخدَم لضبط الحد الأدنى للقيمة (بوحدة dp) التي يجب أن يمتلكها أصغر سمة من سمتَي الشاشة للسماح بالتقسيم بغض النظر عن اتجاه الجهاز.
      • setMaxAspectRatioInPortrait(): يضبط الحد الأقصى لنسبة العرض إلى الارتفاع للشاشة (الارتفاع:العرض) في الوضع عمودي الذي يتم عرض تقسيمات الأنشطة فيه. ملاحظة: القيمة التلقائية هي 1.4، ما يؤدي إلى ملء الأنشطة لشاشة المهام في الوضع العمودي على معظم الأجهزة اللوحية. يمكنك أيضًا الاطّلاع على SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT و setMaxAspectRatioInLandscape(). القيمة التلقائية للوضع الأفقي هي ALWAYS_ALLOW.
      • setFinishPrimaryWithPlaceholder(): يضبط كيفية تأثُّر الأنشطة في الحاوية الأساسية عند إنهاء النشاط النائب. يشير ALWAYS إلى أنّه يجب على النظام دائمًا إنهاء الأنشطة في الحاوية الأساسية عند انتهاء العنصر النائب (راجِع إنهاء الأنشطة).
      • setSticky(): لتحديد ما إذا كان نشاط العنصر النائب سيظهر أعلى حزمة الأنشطة على الشاشات الصغيرة بعد أن يظهر العنصر النائب أولاً في قسم بحد أدنى مناسب للعرض.
    4. أضِف القاعدة إلى WindowManager RuleController:

      Kotlin

      ruleController.addRule(splitPlaceholderRule)

      Java

      ruleController.addRule(splitPlaceholderRule);
  3. حدِّد الأنشطة التي يجب ألا تكون جزءًا من أيّ عملية تقسيم:

    1. أنشئ ActivityFilter يحدِّد نشاطًا يجب أن يشغل دائمًا مساحة عرض المهمة بالكامل:

      Kotlin

      val expandedActivityFilter = ActivityFilter(
        ComponentName(this, ExpandedActivity::class.java),
        null
      )

      Java

      ActivityFilter expandedActivityFilter = new ActivityFilter(
        new ComponentName(this, ExpandedActivity.class),
        null
      );
    2. أضِف الفلتر إلى مجموعة فلاتر:

      Kotlin

      val expandedActivityFilterSet = setOf(expandedActivityFilter)

      Java

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);
    3. أنشئ ActivityRule:

      Kotlin

      val activityRule = ActivityRule.Builder(expandedActivityFilterSet)
          .setAlwaysExpand(true)
          .build()

      Java

      ActivityRule activityRule = new ActivityRule.Builder(
          expandedActivityFilterSet
      ).setAlwaysExpand(true)
       .build();

      ActivityRule.Builder لإنشاء القاعدة وضبطها:

      • expandedActivityFilterSet: يحتوي على فلاتر الأنشطة التي تحدد وقت تطبيق القاعدة من خلال تحديد الأنشطة التي تريد استبعادها من التقسيمات.
      • setAlwaysExpand(): لتحديد ما إذا كان يجب أن يملؤه النشاط نافذة المهمة بأكملها.
    4. أضِف القاعدة إلى WindowManager RuleController:

      Kotlin

      ruleController.addRule(activityRule)

      Java

      ruleController.addRule(activityRule);

التضمين في تطبيقات متعددة

في الإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يمكن للتطبيقات تضمين أنشطة من التطبيقات الأخرى. يتيح تضمين الأنشطة على مستوى التطبيقات أو على مستوى UID دمج الأنشطة المرئية من تطبيقات Android متعددة. يعرض النظام نشاطًا للتطبيق المضيف ونشاطًا مضمّنًا من تطبيق آخر على الشاشة جنبًا إلى جنب أو في أعلى الشاشة أو أسفلها تمامًا كما هو الحال في تضمين نشاط تطبيق واحد.

على سبيل المثال، يمكن أن يدمج تطبيق "الإعدادات" نشاط أداة اختيار الخلفية من تطبيق WallpaperPicker:

الشكل 14. تطبيق "الإعدادات" (القائمة على يمين الشاشة) مع أداة اختيار الخلفية كنشاط مضمّن (على يمين الشاشة)

نموذج الثقة

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

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

المضيفون الموثوق بهم

للسماح للتطبيقات الأخرى بتضمين الأنشطة من تطبيقك والتحكّم بشكل كامل في عرضها، حدِّد شهادة SHA-256 للتطبيق المضيف في سمة android:knownActivityEmbeddingCerts لعنصرَي <activity> أو <application> في ملف بيان تطبيقك.

اضبط قيمة android:knownActivityEmbeddingCerts إما على هيئة سلسلة:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
    ... />

أو، لتحديد شهادات متعددة، مصفوفة من السلاسل:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
    ... />

التي تشير إلى مورد مثل ما يلي:

<resources>
    <string-array name="known_host_certificate_digests">
      <item>cert1</item>
      <item>cert2</item>
      ...
    </string-array>
</resources>

يمكن لمالكي التطبيقات الحصول على خلاصة شهادة SHA من خلال تشغيل مهمة Gradle signingReport. خلاصة الشهادة هي الملف المرجعي لشهادة SHA-256 بدون الفاصلات المنفصلة. لمزيد من المعلومات، يُرجى الاطّلاع على تشغيل تقرير التوقيع و مصادقة العميل.

المضيفون غير الموثوق بهم

للسماح لأي تطبيق بتضمين أنشطة تطبيقك والتحكّم في عرضها، حدِّد سمة android:allowUntrustedActivityEmbedding في عناصر <activity> أو <application> في ملف بيان التطبيق، على سبيل المثال:

<activity
    android:name=".MyEmbeddableActivity"
    android:allowUntrustedActivityEmbedding="true"
    ... />

القيمة التلقائية للسمة هي false، ما يمنع تضمين النشاط على مستوى التطبيقات المختلفة.

المصادقة المخصّصة

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

استخدِم طريقة ActivityEmbeddingController#isActivityEmbedded() من مكتبة Jetpack WindowManager للتحقّق مما إذا كان المضيف يُضمِّن نشاطك، على سبيل المثال:

Kotlin

fun isActivityEmbedded(activity: Activity): Boolean {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity)
}

Java

boolean isActivityEmbedded(Activity activity) {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity);
}

الحد الأدنى للحجم

يطبّق نظام Android الحد الأدنى للارتفاع والعرض المحدّدَين في عنصر بيان التطبيق <layout> على الأنشطة المضمّنة. إذا لم يحدد أحد التطبيقات الحد الأدنى للارتفاع والعرض، يتم تطبيق القيم التلقائية للنظام (sw220dp).

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

<activity-alias>

لكي تعمل عملية تضمين الأنشطة الموثوق بها أو غير الموثوق بها مع عنصر <activity-alias>، يجب تطبيق android:knownActivityEmbeddingCerts أو android:allowUntrustedActivityEmbedding على النشاط المستهدَف بدلاً من الاسم المعرِّف. تستند السياسة التي تتحقّق من الأمان على خادم النظام إلى العلامات التي تم ضبطها على الهدف، وليس العنوان البديل.

التطبيق المضيف

تنفِّذ التطبيقات المستضيفة عملية تضمين الأنشطة على مستوى التطبيقات بالطريقة نفسها التي تتم بها عملية تضمين الأنشطة على مستوى تطبيق واحد. تحدِّد عناصر SplitPairRule و SplitPairFilter أو ActivityRule وActivityFilter الأنشطة المضمّنة وتقسيمات نافذة المهام. يتم تعريف قواعد التقسيم بشكل ثابت في ملف XML أو أثناء التشغيل باستخدام طلبات بيانات واجهة برمجة التطبيقات لنظام WindowManager في Jetpack.

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

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

يمكن للتطبيق المضيف تضمين أنشطته بدون قيود طالما أنّه يتم بدء الأنشطة في المهمة نفسها.

أمثلة على التقسيم

التقسيم من نافذة ملء الشاشة

الشكل 15. يبدأ النشاط (أ) النشاط (ب) على الجانب.

ولا يلزم إجراء إعادة صياغة. يمكنك تحديد إعدادات التقسيم بشكل ثابت أو أثناء التشغيل، ثم استدعاء Context#startActivity() بدون أي مَعلمات إضافية.

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

التقسيم تلقائيًا

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

الشكل 16. تمّ إنشاء القسم من خلال فتح نشاطَين في الوقت نفسه. يكون نشاط واحد عنصرًا نائبًا.

لإنشاء قسم باستخدام عنصر نائب، أنشئ عنصر نائب واربطه بالنشاط الأساسي:

<SplitPlaceholderRule
    window:placeholderActivityName=".PlaceholderActivity">
    <ActivityFilter
        window:activityName=".MainActivity"/>
</SplitPlaceholderRule>

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

الشكل 17. نشاط تفاصيل الرابط لصفحة معيّنة في التطبيق معروض بمفرده على شاشة صغيرة، ولكن مع نشاط قائمة على شاشة كبيرة.

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

Kotlin

override fun onCreate(savedInstanceState Bundle?) {
    . . .
    RuleController.getInstance(this)
        .addRule(SplitPairRule.Builder(filterSet).build())
    startActivity(Intent(this, DetailActivity::class.java))
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    . . .
    RuleController.getInstance(this)
        .addRule(new SplitPairRule.Builder(filterSet).build());
    startActivity(new Intent(this, DetailActivity.class));
}

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

شاشة كبيرة تعرض النشاط في القائمة والنشاط التفصيلي جنبًا إلى جنب
          لا يمكن للتنقّل للخلف إغلاق النشاط التفصيلي ومغادرة النشاط
          في القائمة على الشاشة.

شاشة صغيرة تعرض النشاط التفصيلي فقط لا يمكن للتنقّل للخلف
          إغلاق النشاط التفصيلي وعرض النشاط في القائمة.

بدلاً من ذلك، يمكنك إنهاء كلا النشاطَين في الوقت نفسه باستخدام سمة finishPrimaryWithSecondary:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".ListActivity"
        window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>

راجِع قسم سمات الإعداد.

أنشطة متعدّدة في حاويات مجزّأة

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

الشكل 18. تم فتح النشاط في مكانه في اللوحة الثانوية من نافذة المهام.

Kotlin

class DetailActivity {
    . . .
    fun onOpenSubDetail() {
      startActivity(Intent(this, SubDetailActivity::class.java))
    }
}

Java

public class DetailActivity {
    . . .
    void onOpenSubDetail() {
        startActivity(new Intent(this, SubDetailActivity.class));
    }
}

يتم وضع النشاط الفرعي التفصيلي فوق النشاط التفصيلي، ما يؤدي إلى إخفائه:

يمكن للمستخدم بعد ذلك الرجوع إلى مستوى التفاصيل السابق من خلال الانتقال مجددًا من خلال الحزمة:

الشكل 19. تمّت إزالة النشاط من أعلى الحزمة.

إنّ تجميع الأنشطة فوق بعضها هو السلوك التلقائي عند بدء الأنشطة من نشاط في الحاوية الثانوية نفسها. تنتهي الأنشطة التي يتم إطلاقها من الحاوية الأساسية ضمن قسم نشط أيضًا في الحاوية الثانوية في أعلى مجموعة الأنشطة.

الأنشطة في مهمة جديدة

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

الشكل 20. ابدأ النشاط "ج" في مهمة جديدة من النشاط "ب".

استبدال النشاط

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

الشكل 21. نشاط التنقّل على المستوى الأعلى في اللوحة الأساسية يحلّ محلّ أنشطة الوجهة في اللوحة الثانوية.

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

يجب إزالة الشاشة "أ" من الحزمة الخلفية في هذه الحالات.

السلوك التلقائي عند الإطلاق على الجانب في حاوية جديدة فوق div مقسّم حالي هو وضع الحاويات الثانوية الجديدة في الأعلى والاحتفاظ بالقديمة في الحزمة الخلفية. يمكنك ضبط عمليات التقسيم لمحو الحِزم الثانوية السابقة باستخدام clearTop وبدء الأنشطة الجديدة بشكلٍ طبيعي.

<SplitPairRule
    window:clearTop="true">
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenA"/>
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>

Kotlin

class MenuActivity {
    . . .
    fun onMenuItemSelected(selectedMenuItem: Int) {
        startActivity(Intent(this, classForItem(selectedMenuItem)))
    }
}

Java

public class MenuActivity {
    . . .
    void onMenuItemSelected(int selectedMenuItem) {
        startActivity(new Intent(this, classForItem(selectedMenuItem)));
    }
}

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

تقسيمات متعددة

يمكن للتطبيقات توفير تنقّل عميق على عدّة مستويات من خلال إطلاق أنشطة إضافية على الجانب.

عندما يطلق نشاط في حاوية ثانوية نشاطًا جديدًا على الجانب، يتم إنشاء فسحة إعلانية جديدة فوق الفسحة الإعلانية الحالية.

الشكل 22. يبدأ النشاط "ب" النشاط "ج" على الجانب.

تحتوي حزمة التطبيقات التي تمّ إغلاقها مؤخرًا على جميع الأنشطة التي تمّ فتحها سابقًا، لكي يتمكّن المستخدمون من الانتقال إلى المجموعة التجريبية أ/ب بعد الانتهاء من المجموعة ج.

الأنشطة &quot;أ&quot; و&quot;ب&quot; و&quot;ج&quot; في مجموعة يتم تجميع الأنشطة في
          الترتيب التالي من الأعلى إلى الأسفل: ج، ب، أ.

لإنشاء قسم جديد، اطلق النشاط الجديد على جانب الحاوية الثانوية الحالية. حدِّد الإعدادات لكلٍّ من القسمَين A/B وB/C وأطلق النشاط C بشكلٍ طبيعي من B:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
    <SplitPairFilter
        window:primaryActivityName=".B"
        window:secondaryActivityName=".C"/>
</SplitPairRule>

Kotlin

class B {
    . . .
    fun onOpenC() {
        startActivity(Intent(this, C::class.java))
    }
}

Java

public class B {
    . . .
    void onOpenC() {
        startActivity(new Intent(this, C.class));
    }
}

التفاعل مع تغييرات حالة الانقسام

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

الشكل 23. أنشطة مختلفة تتضمّن عناصر واجهة مستخدِم متطابقة من الناحية الوظيفية

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

الشكل 24. عناصر واجهة مستخدِم مكرّرة في عملية تقسيم النشاط

لمعرفة الحالات التي تكون فيها الأنشطة في عملية تقسيم، تحقّق من عملية SplitController.splitInfoList أو سجِّل مستمعًا باستخدام SplitControllerCallbackAdapter لمعرفة التغييرات في حالة التقسيم. بعد ذلك، عدِّل واجهة المستخدم وفقًا لذلك:

Kotlin

val layout = layoutInflater.inflate(R.layout.activity_main, null)
val view = layout.findViewById<View>(R.id.infoButton)
lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance.
            .collect { list ->
                view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
            }
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    . . .
    new SplitControllerCallbackAdapter(SplitController.getInstance(this))
        .addSplitListener(
            this,
            Runnable::run,
            splitInfoList -> {
                View layout = getLayoutInflater().inflate(R.layout.activity_main, null);
                layout.findViewById(R.id.infoButton).setVisibility(
                    splitInfoList.isEmpty() ? View.VISIBLE : View.GONE);
            });
}

يمكن تشغيل مهام التشغيل المتعدّد بشكل متزامن في أيّ حالة من حالات دورة الحياة، ولكن يتم تشغيلها عادةً في حالة STARTED للحفاظ على الموارد (اطّلِع على استخدام مهام التشغيل المتعدّد بشكل متزامن في Kotlin مع المكوّنات المدركِة لدورة الحياة للحصول على مزيد من المعلومات).

يمكن إجراء عمليات الاستدعاء في أيّ حالة من حالات دورة الحياة، بما في ذلك عند إيقاف نشاط. يجب أن يكون المستمعون مسجّلين عادةً في onStart() وغير مسجّلين في onStop().

نافذة مشروطة في وضع ملء الشاشة

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

يمكن فرض ملء النشاط لإطار المهام دائمًا باستخدام إعدادات التمدد:

<ActivityRule
    window:alwaysExpand="true">
    <ActivityFilter
        window:activityName=".FullWidthActivity"/>
</ActivityRule>

إنهاء الأنشطة

يمكن للمستخدمين إنهاء الأنشطة على أي من جانبَي القسم من خلال التمرير سريعًا من حافة الشاشة:

الشكل 25. إيماءة التمرير السريع لإنهاء النشاط "ب"
الشكل 26. إيماءة التمرير السريع لإنهاء النشاط (أ)

إذا تم ضبط الجهاز لاستخدام زر الرجوع بدلاً من التنقّل بالإيماءات، يتم إرسال الإدخال إلى النشاط الذي تم التركيز عليه، أي النشاط الذي تم لمسه أو بدؤه مؤخرًا.

يعتمد تأثير إنهاء جميع الأنشطة في حاوية على الحاوية المقابلة على إعدادات التقسيم.

سمات الضبط

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

  • window:finishPrimaryWithSecondary — كيفية تأثير إنهاء جميع الأنشطة في الحاوية الثانوية في الأنشطة في الحاوية الأساسية
  • window:finishSecondaryWithPrimary — كيفية تأثير إنهاء جميع الأنشطة في الحاوية الأساسية في الأنشطة في الحاوية الثانوية

تشمل القيم المحتمَلة للسمات ما يلي:

  • always - إنهاء الأنشطة دائمًا في الحاوية المرتبطة
  • never - عدم إنهاء الأنشطة في الحاوية المرتبطة مطلقًا
  • adjacent — إنهاء الأنشطة في الحاوية المرتبطة عند عرض الحاويتَين بجانب بعضهما، ولكن ليس عند تجميع الحاويتَين

مثلاً:

<SplitPairRule
    &lt;!-- Do not finish primary container activities when all secondary container activities finish. --&gt;
    window:finishPrimaryWithSecondary="never"
    &lt;!-- Finish secondary container activities when all primary container activities finish. --&gt;
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

الإعدادات التلقائية

عند انتهاء جميع الأنشطة في حاوية واحدة من القسم، تشغل الحاوية المتبقية النافذة بأكملها:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

قسم يتضمّن النشاطَين &quot;أ&quot; و&quot;ب&quot; اكتمل عرض المحتوى &quot;أ&quot;، ما يترك المحتوى &quot;ب&quot; يشغل
          النافذة بأكملها.

قسم يتضمّن النشاطَين &quot;أ&quot; و&quot;ب&quot; اكتمل عرض &quot;ب&quot;، ما أدى إلى التمدد الكامل لعرض &quot;أ&quot;.

إكمال الأنشطة معًا

يمكنك إنهاء الأنشطة في الحاوية الأساسية تلقائيًا عند انتهاء جميع الأنشطة في الحاوية الثانوية:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

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

قسم يحتوي على النشاطَين &quot;أ&quot; و&quot;ب&quot; تم الانتهاء من المهمة &quot;أ&quot;، ما يترك المهمة &quot;ب&quot; وحدها
          في نافذة المهام.

إنهاء الأنشطة في الحاوية الثانوية تلقائيًا عند انتهاء كل الأنشطة في الحاوية الأساسية:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

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

قسم يحتوي على النشاطَين &quot;أ&quot; و&quot;ب&quot; تم الانتهاء من المهمة &quot;ب&quot;، ما يترك المهمة &quot;أ&quot; وحدها
          في نافذة المهام.

إنهاء الأنشطة معًا عند انتهاء جميع الأنشطة في الحاوية الأساسية أو الحاوية الثانوية:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

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

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

إنهاء أنشطة متعددة في الحاويات

في حال تجميع أنشطة متعددة في حاوية مجزّأة، لا يؤدي إنهاء نشاط في أسفل الحزمة إلى إنهاء الأنشطة في الأعلى تلقائيًا.

على سبيل المثال، إذا كان هناك نشاطان في الحاوية الثانوية، "ج" فوق "ب":

مجموعة الأنشطة الثانوية التي تحتوي على النشاط &quot;ج&quot; فوق النشاط &quot;ب&quot;
          فوق مجموعة الأنشطة الأساسية التي تحتوي على النشاط
          &quot;أ&quot;

ويتم تحديد إعداد التقسيم من خلال إعداد الأنشطة أ و ب:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

يؤدي إنهاء النشاط الأعلى إلى الاحتفاظ بالقسمة.

تم تقسيم النشاط &quot;أ&quot; في الحاوية الأساسية والنشاطَين &quot;ب&quot; و&quot;ج&quot; في الحاوية
          الثانوية، مع تجميع &quot;ج&quot; فوق &quot;ب&quot;. ينتهي القسم &quot;ج&quot;، ما يترك &quot;أ&quot; و&quot;ب&quot; في عملية تقسيم النشاط.

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

تم تقسيم النشاط &quot;أ&quot; في الحاوية الأساسية والنشاطَين &quot;ب&quot; و&quot;ج&quot; في الحاوية
          الثانوية، مع تجميع &quot;ج&quot; فوق &quot;ب&quot;. ينتهي النشاط &quot;ب&quot;، ما يترك النشاطَين &quot;أ&quot; و&quot;ج&quot; في عملية تقسيم النشاط.

يتم أيضًا تنفيذ أي قواعد إضافية لإنهاء الأنشطة معًا، مثل إنهاء النشاط الثانوي مع النشاط الأساسي:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

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

وعند ضبط عملية التقسيم لإنهاء القسمَين الأساسي والثانوي معًا:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

تم تقسيم النشاط &quot;أ&quot; في الحاوية الأساسية والنشاطَين &quot;ب&quot; و&quot;ج&quot; في الحاوية
          الثانوية، مع تجميع &quot;ج&quot; فوق &quot;ب&quot;. ينتهي المسار &quot;ج&quot;، ما يترك &quot;أ&quot; و&quot;ب&quot; في عملية تقسيم النشاط.

تم تقسيم النشاط &quot;أ&quot; في الحاوية الأساسية والنشاطَين &quot;ب&quot; و&quot;ج&quot; في الحاوية
          الثانوية، مع تجميع &quot;ج&quot; فوق &quot;ب&quot;. ينتهي النشاط &quot;ب&quot;، ما يترك النشاطَين &quot;أ&quot; و&quot;ج&quot; في عملية تقسيم النشاط.

تم تقسيم النشاط &quot;أ&quot; في الحاوية الأساسية والنشاطَين &quot;ب&quot; و&quot;ج&quot; في الحاوية
          الثانوية، مع تجميع &quot;ج&quot; فوق &quot;ب&quot;. تنتهي عملية A، ما يؤدي أيضًا إلى إنهاء عملية B و
          عملية C.

تغيير خصائص التقسيم أثناء التشغيل

لا يمكن تغيير سمات التقسيم النشط والمرئي. يؤثّر تغيير قواعد التقسيم في عمليات إطلاق الأنشطة الإضافية والحاويات الجديدة، ولكن ليس في عمليات التقسيم الحالية والنشطة.

لتغيير سمات التقسيمات النشطة، عليك إنهاء النشاط الجانبي أو الأنشطة في التقسيم وبدء الاختبار الجانبي مرة أخرى بإعدادات جديدة.

خصائص التقسيم الديناميكي

يتوافق نظام التشغيل Android 15 (المستوى 35 من واجهة برمجة التطبيقات) والإصدارات الأحدث مع الإصدار 1.4 من WindowManager في Jetpack والإصدارات الأحدث، ويقدّم هذان الإصداران ميزات ديناميكية تتيح إمكانية ضبط عمليات دمج الأنشطة، بما في ذلك:

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

توسيع اللوحة

يتيح توسيع اللوحة للمستخدمين تعديل مقدار مساحة الشاشة المخصّصة للنشاطَين في تنسيق اللوحة المزدوجة.

لتخصيص مظهر مقسم النافذة وضبط النطاق القابل للسحب للمقسّم، اتّبِع الخطوات التالية:

  1. إنشاء مثيل من DividerAttributes

  2. تخصيص سمات المقسم:

    • color: لون فاصل اللوحة القابلة للسحب.

    • widthDp: عرض فاصل اللوحة القابلة للسحب اضبط القيمة على WIDTH_SYSTEM_DEFAULT للسماح للنظام بتحديد عرض الفاصل.

    • نطاق السحب: الحد الأدنى للنسبة المئوية التي يمكن أن يشغلها أي من اللوحة على الشاشة يمكن أن تتراوح بين 0.33 و0.66. اضبط القيمة على DRAG_RANGE_SYSTEM_DEFAULT للسماح للنظام بتحديد نطاق arrast.

Kotlin

val splitAttributesBuilder: SplitAttributes.Builder = SplitAttributes.Builder()
    .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
    .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)

if (WindowSdkExtensions.getInstance().extensionVersion >= 6) {
    splitAttributesBuilder.setDividerAttributes(
      DividerAttributes.DraggableDividerAttributes.Builder()
        .setColor(getColor(context, R.color.divider_color))
        .setWidthDp(4)
        .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
        .build()
    )
}
val splitAttributes: SplitAttributes = splitAttributesBuilder.build()

Java

SplitAttributes.Builder splitAttributesBuilder = new SplitAttributes.Builder()
    .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
    .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT);

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    splitAttributesBuilder.setDividerAttributes(
      new DividerAttributes.DraggableDividerAttributes.Builder()
        .setColor(ContextCompat.getColor(context, R.color.divider_color))
        .setWidthDp(4)
        .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
        .build()
    );
}
SplitAttributes splitAttributes = splitAttributesBuilder.build();

تثبيت مجموعة الأنشطة

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

لتفعيل تثبيت حِزم الأنشطة في تطبيقك، اتّبِع الخطوات التالية:

  1. أضِف زرًا إلى ملف تنسيق النشاط الذي تريد تثبيته، مثلاً، النشاط التفصيلي لتنسيق قائمة التفاصيل:

    <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/detailActivity"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/white"
     tools:context=".DetailActivity">
    
    <TextView
       android:id="@+id/textViewItemDetail"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textSize="36sp"
       android:textColor="@color/obsidian"
       app:layout_constraintBottom_toTopOf="@id/pinButton"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
    
    <androidx.appcompat.widget.AppCompatButton
       android:id="@+id/pinButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/pin_this_activity"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. في طريقة onCreate() للنشاط، اضبط أداة معالجة onclick على الزر:

    Kotlin

    pinButton = findViewById(R.id.pinButton)
    pinButton.setOnClickListener {
        val splitAttributes: SplitAttributes = SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build()
    
        val pinSplitRule = SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build()
    
        SplitController.getInstance(applicationContext).pinTopActivityStack(taskId, pinSplitRule)
    }

    Java

    Button pinButton = findViewById(R.id.pinButton);
    pinButton.setOnClickListener( (view) => {
        SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();
    
        SplitPinRule pinSplitRule = new SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build();
    
        SplitController.getInstance(getApplicationContext()).pinTopActivityStack(getTaskId(), pinSplitRule);
    });

تعتيم مربّع الحوار بملء الشاشة

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

باستخدام الإصدار 1.4 من WindowManager والإصدارات الأحدث، يتم تعتيم نافذة التطبيق بالكامل تلقائيًا عند فتح مربّع حوار (راجِع EmbeddingConfiguration.DimAreaBehavior.ON_TASK).

لتعتيم حاوية النشاط الذي فتح مربّع الحوار فقط، استخدِم EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK.

استخراج نشاط من نافذة مُقسَّمة إلى نافذة ملء الشاشة

أنشئ إعدادًا جديدًا يعرض نافذة النشاط الجانبي بالكامل، ثم أعِد تشغيل النشاط باستخدام نية تؤدي إلى المثيل نفسه.

التحقّق من توفّر ميزة "التقسيم" أثناء التشغيل

تتوفّر ميزة تضمين الأنشطة على نظام التشغيل Android 12L (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأحدث، ولكنها متوفرة أيضًا على بعض الأجهزة التي تعمل بإصدارات أقدم من نظام التشغيل. للتحقّق من توفّر الميزة أثناء التشغيل، استخدِم السمة SplitController.splitSupportStatus أو الطريقة SplitController.getSplitSupportStatus():

Kotlin

if (SplitController.getInstance(this).splitSupportStatus ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

Java

if (SplitController.getInstance(this).getSplitSupportStatus() ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

إذا لم تكن عمليات التقسيم متاحة، يتم تشغيل الأنشطة على قمة ملف activity المجمّع (باتّباع نموذج تضمين غير الأنشطة).

منع تجاوز إعدادات النظام

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

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

يمكن لتطبيقك منع تضمين نشاط النظام أو السماح به من خلال ضبط سمة في ملف بيان التطبيق، على سبيل المثال:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <property
            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
            android:value="true|false" />
    </application>
</manifest>

يتم تحديد اسم السمة في كائن Jetpack WindowManager WindowProperties. اضبط القيمة على false إذا كان تطبيقك ينفّذ عملية تضمين النشاط، أو إذا كنت تريد منع النظام من تطبيق قواعد تضمين النشاط على تطبيقك. اضبط القيمة على true للسماح للنظام بتطبيق عملية تضمين النشاط التي يحدّدها النظام على تطبيقك.

القيود والشروط والتحذيرات

  • لا يمكن إلا للتطبيق المضيف للمهمة، والذي يتم تحديده على أنّه مالك النشاط الجذر في المهمة، تنظيم الأنشطة الأخرى وتضمينها في المهمة. إذا كانت الأنشطة التي تتيح الدمج والفواصل تعمل في مهمة تنتمي إلى تطبيق مختلف، لن تعمل ميزة الدمج والفواصل مع هذه الأنشطة.
  • لا يمكن تنظيم الأنشطة إلا ضمن مهمة واحدة. يؤدي إطلاق نشاط في مهمة جديدة إلى وضعه دائمًا في نافذة موسّعة جديدة خارج أي عمليات تقسيم حالية.
  • لا يمكن تنظيم الأنشطة في العملية نفسها إلا ووضعها في قسم. لا يُبلغ الإجراء المرجعي SplitInfo إلا عن الأنشطة التي تنتمي إلى العملية نفسها، لأنّه لا تتوفّر طريقة لمعرفة الأنشطة في العمليات المختلفة.
  • لا ينطبق كل زوج أو قاعدة نشاط فردية إلا على عمليات بدء النشاط التي تحدث بعد تسجيل القاعدة. لا تتوفّر حاليًا طريقة لتعديل عمليات التقسيم الحالية أو خصائصها المرئية.
  • يجب أن تتطابق إعدادات فلتر الأزواج المجزّأة مع النوايا المستخدَمة عند بدء الأنشطة بالكامل. يحدث المطابقة عند بدء نشاط جديد من عملية التطبيق، لذا قد لا يعرف أسماء المكوّنات التي يتم حلّها لاحقًا في عملية النظام عند استخدام المقصودات الضمنية. إذا لم يكن اسم المكوّن معروفًا في وقت الإطلاق، يمكن استخدام بدلاء بدلاً من ذلك ("*/*") ويمكن إجراء الفلترة استنادًا إلى إجراء النية.
  • لا تتوفّر حاليًا طريقة لنقل الأنشطة بين الحاويات أو بين عمليات التقسيم والخروج منها بعد إنشائها. لا تنشئ مكتبة WindowManager تقسيمات إلا عند بدء أنشطة جديدة تتضمّن قواعد مطابقة، ويتم إتلاف التقسيمات عند انتهاء النشاط الأخير في حاوية مجزّأة.
  • يمكن إعادة تشغيل الأنشطة عند تغيير الإعدادات، لذا عند إنشاء أو إزالة فاصل وتغيير حدود النشاط، يمكن أن يخضع النشاط لعملية تدمير النسخة السابقة بالكامل وإنشاء النسخة الجديدة. ونتيجةً لذلك، على مطوّري التطبيقات الانتباه إلى إجراءات مثل بدء أنشطة جديدة من عمليات الاستدعاء في دورة الحياة.
  • يجب أن تتضمّن الأجهزة واجهة إضافات النوافذ لتتمكّن من تضمين النشاط. تتضمّن الواجهة جميع الأجهزة التي تعمل بالإصدار 12L من نظام التشغيل Android (المستوى 32 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث تقريبًا، والتي تعمل بشاشة كبيرة. ومع ذلك، لا تتضمّن بعض الأجهزة ذات الشاشات الكبيرة التي لا يمكنها تشغيل أنشطة متعدّدة واجهة تطبيقات إضافية للنوافذ. إذا كان جهاز الشاشة الكبيرة لا يتيح استخدام وضع المتعدّد النوافذ، قد لا يتيح تضمين الأنشطة.

مصادر إضافية