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

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

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

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

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

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

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

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

تقسيم نافذة المهمة

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

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

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

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

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

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

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

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

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

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

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

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

التنقّل للخلف

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

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

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

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

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

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

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

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

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

تتيح لك مكتبة Jetpack WindowManager إنشاء نشاط يضمّ تنسيقًا متعدّد الأقسام على الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار Android 12L (المستوى 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. قسمان من النشاط معروضان من اليمين إلى اليسار ولكن بنِسب تقسيم مختلفة

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

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

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

الشكل 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"
    ... />

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

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

للحدّ من مخاطر تضمين الأنشطة غير الموثوق بها، أنشئ أسلوب مصادقة مخصّصًا يقوّم بإثبات هوية المضيف. إذا كنت تعرف شهادات المضيف، استخدِم مكتبة 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 أو أثناء التشغيل باستخدام طلبات Jetpack WindowManager API.

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

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

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

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

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

الشكل 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. نشاط التنقّل على المستوى الأعلى في اللوحة الأساسية يستبدل أنشطة الوجهة في اللوحة الثانوية.

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

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

السلوك التلقائي عند الإطلاق على الجانب في حاوية جديدة فوق شاشة مقسمة حالية هو وضع الحاويات الثانوية الجديدة في الأعلى والاحتفاظ بالحاويات القديمة في الحزمة الخلفية. يمكنك ضبط عمليات التقسيم لمحو الحِزم الثانوية السابقة باستخدام 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. يبدأ النشاط "ب" النشاط "ج" على الجانب.

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

الأنشطة &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().

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

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

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

<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; وحدها
          في نافذة المهام.

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

<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;، ما يترك نافذة المهام فارغة.

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

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

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

مجموعة الأنشطة الثانوية التي تحتوي على النشاط &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 للسماح للنظام بتحديد نطاق السحب.

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.

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

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

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

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

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

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

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

مصادر إضافية