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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • بجانب أي نشاط آخر:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

يبدأ النشاط "ب" النشاط "ج" إلى الجانب وينقل القسمة إلى الجانبين:

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

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

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

في نافذة مهمة أصغر، يتقلص التطبيق إلى نشاط واحد مع تشغيل 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> في ملف بيان التطبيق، وتعيين إلى "صواب"، على سبيل المثال:

    <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>
    

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

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

إعدادات 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 ويُتيح القواعد للنظام. الحقيبة النفاثة تجعل مكتبة بدء التشغيل 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(): يضبط أدنى قيمة (بوحدة بكسل مستقلة الكثافة) التي ويجب أن يفعّل حجم أصغر من بُعدي العرض تقسيم الشاشة بغض النظر عن اتجاه الجهاز.
      • setMaxAspectRatioInPortrait(): لضبط الحدّ الأقصى لأبعاد العرض النسبة (height:width) في الاتجاه العمودي الذي يعرض فيه النشاط يتم عرض التقسيمات. إذا كانت نسبة العرض إلى الارتفاع لشاشة في الوضع العمودي تتعدى الحد الأقصى لنسبة العرض إلى الارتفاع، يتم إيقاف عمليات التقسيم بغض النظر عن عرض الشاشة. ملاحظة: القيمة التلقائية هي 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(): لضبط الحد الأدنى لعرض العرض (بوحدات بكسل مستقلة الكثافة، وحدة بكسل مستقلة الكثافة) تسمح بالتقسيم.
      • setMinSmallestWidthDp(): تُستخدَم لضبط الحد الأدنى للقيمة (بالوحدة dp) التي يجب أن يمتلكها أصغر سمة من سمتَي الشاشة للسماح بالتقسيم بغض النظر عن اتجاه الجهاز.
      • setMaxAspectRatioInPortrait(): لضبط الحد الأقصى لنسبة العرض إلى الارتفاع في العرض (height:width) في الوضع العمودي الاتجاه الذي يتم عرض تقسيمات الأنشطة له. ملاحظة: القيمة التلقائية هي 1.4، ما يؤدي إلى ملء الأنشطة لشاشة مهمة بالوضع العمودي على معظم الأجهزة اللوحية. يمكن أيضًا مراجعة SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT و setMaxAspectRatioInLandscape() القيمة التلقائية للوضع الأفقي هي ALWAYS_ALLOW.
      • setFinishPrimaryWithPlaceholder(): يضبط كيفية تأثير إنهاء النشاط النائب في الأنشطة في الحاوية الأساسية. تشير "دائمًا" إلى أن النظام يجب دائمًا إنهاء الأنشطة في الحاوية الأساسية عندما يكون العنصر النائب النهاية (راجع إنهاء الأنشطة).
      • 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);

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

في نظام التشغيل Android 13 (المستوى 33 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يمكن للتطبيقات تضمين أنشطة من التطبيقات. تضمين الأنشطة في جميع التطبيقات أو المعرّف الفريد يتيح التكامل المرئي للأنشطة من تطبيقات 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 أو في وقت التشغيل باستخدام Jetpack طلبات بيانات من واجهة برمجة تطبيقات WindowManager.

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

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

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

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

تقسيم الفيديو من وضع ملء الشاشة

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

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

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

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

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

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

لإنشاء قسم جديد، عليك تفعيل النشاط الجديد بجانب القسم الحالي. الحاوية الثانوية. يُرجى توضيح الإعدادات لكل من قسمَي A/B وB/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. يمكنك التمرير سريعًا لإنهاء النشاط "أ".

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

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

سمات الضبط

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

  • 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;. اكتمل عرض A، ما أدى إلى التمرير بمحتوى B
          إلى أن يشغل النافذة بأكملها.

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

إنهاء الأنشطة معًا

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

تم تقسيم النشاط &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; في
          الثانوية، C مكدسة فوق B. ينتهي بـ C، ويترك A وB في
          تقسيم النشاط.

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

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

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

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

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

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

يتوافق نظام التشغيل 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.
}

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

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

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

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

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

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

مصادر إضافية