يساعد تضمين النشاط في تحسين التطبيقات على الأجهزة ذات الشاشات الكبيرة من خلال تقسيم نافذة مهام التطبيق بين نشاطين أو حالتين من نفس النشاط.
إذا كان تطبيقك يتألّف من أنشطة متعدّدة، سيتيح لك تضمين النشاط توفير تجربة محسّنة للمستخدم على الأجهزة اللوحية والأجهزة القابلة للطي وأجهزة ChromeOS.
لا يتطلب تضمين الأنشطة إعادة هيكلة الرمز. يمكنك تحديد كيفية عرض تطبيقك لأنشطته جنبًا إلى جنب أو بشكلٍ مجمّع من خلال إنشاء ملف إعداد XML أو إجراء طلبات بيانات من واجهة برمجة التطبيقات Jetpack WindowManager.
يتم الحفاظ على دعم الشاشات الصغيرة تلقائيًا. عندما يكون تطبيقك على جهاز بشاشة صغيرة، تكون الأنشطة مكدسة واحدة فوق الأخرى. على الشاشات الكبيرة، يتم عرض الأنشطة جنبًا إلى جنب. يحدد النظام العرض التقديمي بناءً على التهيئة التي قمت بإنشائها - لا حاجة إلى منطق تشعّبي.
تسمح ميزة "تضمين الأنشطة" بالتغيرات في اتجاه الجهاز وتعمل بسلاسة على الأجهزة القابلة للطي، فضلاً عن أنشطة تكديس الجهاز وفك تكديسه عند طي الجهاز وفتحه.
تتوفّر ميزة تضمين الأنشطة على معظم الأجهزة ذات الشاشات الكبيرة التي تعمل بنظام التشغيل Android 12L (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأحدث.
تقسيم نافذة المَهمّة
يؤدي تضمين النشاط إلى تقسيم نافذة مهام التطبيق إلى حاويتين: أساسية وثانوية. تحتوي الحاويات على أنشطة تم إطلاقها من النشاط الرئيسي أو من أنشطة أخرى موجودة بالفعل في الحاويات.
يتم تكديس الأنشطة في الحاوية الثانوية عند إطلاقها، ويتم تكديس الحاوية الثانوية على الحاوية الأساسية على الشاشات الصغيرة، بحيث يتوافق تكديس الأنشطة والتنقل الخلفي مع ترتيب الأنشطة المضمّنة بالفعل في تطبيقك.
يتيح لك تضمين الأنشطة عرض الأنشطة بعدة طرق. يمكن لتطبيقك تقسيم نافذة المهام عن طريق تشغيل نشاطين جنبًا إلى جنب في الوقت نفسه:
أو، يمكن أن يؤدي النشاط الذي يشغل نافذة المهمة بأكملها إلى إنشاء تقسيم عن طريق إطلاق نشاط جديد إلى جانب:
يمكن للأنشطة المنقسمة بالفعل والتي تتم مشاركتها في نافذة مهمة تشغيل أنشطة أخرى بالطرق التالية:
بجانب نشاط آخر:
إلى الجانب، وتحويل القسمين إلى الجانبين، وإخفاء النشاط الأساسي السابق:
إطلاق نشاط في مكانه في الأعلى؛ أي في نفس حزمة الأنشطة:
لتشغيل نافذة كاملة لنشاط في المهمة نفسها:
الرجوع إلى الصفحة السابقة
يمكن أن يكون للأنواع المختلفة من التطبيقات قواعد مختلفة للرجوع إلى الوراء في حالة نافذة مهمة مقسمة بناءً على التبعيات بين الأنشطة أو كيفية تشغيل المستخدمين للحدث الخلفي، على سبيل المثال:
- التنفيذ معًا: إذا كانت الأنشطة مرتبطة ببعضها، وكان يجب عدم عرض أحدهما بدون الآخر، يمكن ضبط ميزة التنقل للخلف لإنهاء الاثنين.
- تنفيذ الأمر بمفردك: إذا كانت الأنشطة مستقلة تمامًا، لا يؤثر التنقل للخلف في النشاط في حالة نشاط آخر في نافذة المهمة.
يتم إرسال الحدث العكسي إلى آخر نشاط محل التركيز عند استخدام التنقّل باستخدام الأزرار. باستخدام التنقل القائم على الإيماءات، يتم إرسال الحدث الخلفي إلى النشاط الذي حدثت فيه الإيماءة.
تصميم متعدد الأجزاء
يتيح لك Jetpack WindowManager إنشاء نشاط يتضمّن تنسيقًا متعدد الأجزاء على الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار 12L من نظام التشغيل Android (المستوى 32 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث، وعلى بعض الأجهزة التي تتضمّن إصدارات سابقة من النظام الأساسي. يمكن للتطبيقات الحالية التي تستند إلى أنشطة متعددة بدلاً من الأجزاء أو التنسيقات المستندة إلى العرض، مثل SlidingPaneLayout
، تحسين تجربة المستخدم على الشاشات الكبيرة بدون إعادة تنظيم رمز المصدر.
من الأمثلة الشائعة تقسيم القائمة مع التفاصيل. لضمان عرض تقديمي عالي الجودة، يبدأ النظام نشاط القائمة، ثم يبدأ التطبيق نشاط التفاصيل على الفور. ينتظر نظام الانتقال حتى يتم رسم كلا النشاطين، ثم يعرضهما معًا. بالنسبة للمستخدم، يتم تشغيل النشاطين كواحد.
تقسيم السمات
يمكنك تحديد كيفية متناسبة نافذة المهام بين الحاويات المقسمة وكيفية وضع الحاويات بالنسبة إلى بعضها البعض.
بالنسبة إلى القواعد المحدّدة في ملف إعداد 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 أدناه للحصول على أمثلة.
العناصر النائبة
أنشطة العنصر النائب هي أنشطة ثانوية فارغة تشغل مساحة من تقسيم النشاط. من المفترض في النهاية استبدالها بنشاط آخر يحتوي على محتوى. على سبيل المثال، يمكن أن يشغل نشاط العنصر النائب الجانب الثانوي من تقسيم النشاط بتنسيق قائمة تفاصيل حتى يتم تحديد عنصر من القائمة، وعندها يتم استبدال العنصر النائب بنشاط يحتوي على المعلومات التفصيلية لعنصر القائمة المحدد.
لا يعرض النظام تلقائيًا العناصر النائبة إلا عند توفّر مساحة كافية لتقسيم النشاط. تنتهي العناصر النائبة تلقائيًا عندما يتغير حجم العرض إلى عرض أو ارتفاع صغير جدًا بحيث لا يمكن عرض تقسيم. عندما تسمح المساحة، يعيد النظام تشغيل العنصر النائب مع إعادة تهيئة.
مع ذلك، يمكن أن تلغي السمة stickyPlaceholder
لطريقة SplitPlaceholderRule
أو setSticky()
في SplitPlaceholder.Builder
السلوك التلقائي. عندما تحدّد السمة أو الطريقة قيمة true
، يعرض النظام العنصر النائب بصفته أعلى نشاط في نافذة المهام عندما يتم تغيير حجم الشاشة إلى عرض أحادي اللوحة من عرض مكوّن من لوحتَين (راجِع ضبط الإعدادات للاطّلاع على مثال).
التغييرات في حجم النوافذ
عندما تؤدي التغييرات في إعدادات الجهاز إلى تقليل عرض نافذة المهام بحيث لا يكون حجمها كبيرًا بما يكفي لاستخدام تنسيق متعدد الأجزاء (على سبيل المثال، عندما يتم طي شاشة كبيرة جهاز قابل للطي من حجم الجهاز اللوحي إلى حجم الهاتف أو يتم تغيير حجم نافذة التطبيق في وضع النوافذ المتعددة)، يتم تكديس الأنشطة غير النائبة في الجزء الثانوي من نافذة المهمة فوق الأنشطة في الجزء الأساسي.
لا يتم عرض أنشطة العناصر النائبة إلا عند وجود عرض شاشة كافٍ للتقسيم. في الشاشات الأصغر حجمًا، يتم إغلاق العنصر النائب تلقائيًا. عندما تصبح منطقة العرض كبيرة بما يكفي مرة أخرى، تتم إعادة إنشاء العنصر النائب. (اطّلِع على العناصر النائبة أعلاه.)
يمكن تجميع الأنشطة لأنّ WindowManager يرتب الأنشطة في الجزء الثانوي فوق الأنشطة في الجزء الأساسي.
أنشطة متعددة في اللوحة الثانوية
يبدأ النشاط "ب" النشاط "ج" في مكانه بدون علامات نية إضافية:
مما ينتج عنه الترتيب z التالي للأنشطة في نفس المهمة:
لذلك، في نافذة مهام أصغر، يتقلص التطبيق إلى نشاط واحد مع وجود C في الجزء العلوي من المكدس:
يؤدي الانتقال للخلف في النافذة الأصغر إلى التنقل عبر الأنشطة المكدسة فوق بعضها البعض.
إذا تمت استعادة تهيئة نافذة المهمة إلى حجم أكبر يمكنه استيعاب أجزاء متعددة، يتم عرض الأنشطة جنبًا إلى جنب مرة أخرى.
تقسيمات مكدسة
يبدأ النشاط "ب" النشاط "ج" إلى الجانب ويحول الانقسام إلى الجانبين:
والنتيجة هي الترتيب z التالي للأنشطة في نفس المهمة:
في نافذة مهام أصغر، يتقلص التطبيق إلى نشاط واحد مع وجود حرف C في الأعلى:
اتجاه عمودي ثابت
يتيح إعداد البيان android:screenOrientation للتطبيقات تقييد الأنشطة باتجاه عمودي أو أفقي. لتحسين تجربة المستخدم على الأجهزة ذات الشاشات الكبيرة، مثل الأجهزة اللوحية والأجهزة القابلة للطي، يمكن للمصنّعين الأصليين للأجهزة (OEM) تجاهل طلبات اتجاه الشاشة والشاشة الأفقية للتطبيق في الاتجاه العمودي على الشاشات الأفقية أو الاتجاه الأفقي على الشاشات العمودية.
وبالمثل، عندما تكون ميزة "تضمين النشاط" مفعّلة، يمكن للمصنّعين الأصليين للأجهزة تخصيص الأجهزة للأنشطة العمودية الثابتة في الوضع الأفقي على الشاشات الكبيرة (أكبر من 600 بكسل مستقل الكثافة). عند بدء نشاط ثانٍ في الوضع العمودي الثابت، يمكن للجهاز عرض النشاطَين جنبًا إلى جنب في عرض مؤلف من جزأين.
أضِف السمة android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
دائمًا إلى ملف بيان التطبيقات لإعلام الأجهزة بأنّ تطبيقك يتيح تضمين الأنشطة (راجِع إعدادات التقسيم أدناه). ويمكن للأجهزة المخصّصة التي يقدّمها المصنّعون الأصليون بعد ذلك تحديد ما إذا كان سيتم استخدام الأنشطة في الوضع العمودي الثابت للعرض على شاشة عريضة أفقيًا أم لا.
إعدادات التقسيم
تعمل قواعد التقسيم على ضبط عمليات تقسيم الأنشطة. يمكنك تحديد قواعد التقسيم في ملف إعداد XML أو من خلال إجراء طلبات البيانات من واجهة برمجة التطبيقات في Jetpack WindowManager.
وفي كلتا الحالتين، يجب أن يصل تطبيقك إلى مكتبة WindowManager وأن يُعلِم النظام بأنّ التطبيق قد نفَّذ عملية تضمين الأنشطة.
قم بما يلي:
أضِف أحدث تبعية لمكتبة WindowManager إلى ملف
build.gradle
على مستوى الوحدة في تطبيقك، على سبيل المثال:implementation 'androidx.window:window:1.1.0-beta02'
توفّر مكتبة WindowManager جميع المكوّنات المطلوبة لتضمين النشاط.
أبلِغ النظام بأنّ تطبيقك قد نفَّذ عملية تضمين النشاط.
أضِف السمة
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>
في الإصدار 1.1.0-alpha06 والإصدارات الأحدث من WindowManager، يتم إيقاف تقسيمات تضمين الأنشطة ما لم تتم إضافة الخاصية إلى البيان وضبطها على "صحيح".
وتستخدم الشركات المصنّعة للأجهزة أيضًا هذا الإعداد لتفعيل إمكانات مخصّصة للتطبيقات التي تتيح تضمين النشاط. على سبيل المثال، يمكن للأجهزة إبقاء النشاط في الوضع العمودي فقط في وضع العرض الأفقي لتوجيه النشاط للانتقال إلى تنسيق من لوحتين عند بدء نشاط ثانٍ (راجِع الاتجاه العمودي الثابت).
إعدادات XML
لإنشاء عملية تنفيذ مستندة إلى XML لتضمين النشاط، أكمِل الخطوات التالية:
أنشئ ملف موارد 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>
إنشاء م إعداد.
يحلّل مكوّن WindowManager
RuleController
ملف الإعداد بتنسيق XML ويتيح القواعد للنظام. توفّر مكتبة البدء في JetpackInitializer
ملف XML لـRuleController
عند بدء تشغيل التطبيق، بحيث تسري القواعد عند بدء أي أنشطة.لإنشاء مهيئ، قم بما يلي:
أضِف أحدث تبعية لمكتبة Jetpack Startup إلى ملف
build.gradle
على مستوى الوحدة، على سبيل المثال:implementation 'androidx.startup:startup-runtime:1.1.1'
إنشاء فئة تنفّذ واجهة
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(); } }
أنشئ موفّر محتوى لتعريفات القواعد.
يمكنك إضافة
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
لضمان سريان القواعد قبل بدء أي أنشطة.
لإنشاء تقسيم نشاط آليًا، اتّبِع الخطوات التالية:
إنشاء قاعدة تقسيم:
أنشئ
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 );
أضِف الفلتر إلى مجموعة فلاتر:
Kotlin
val filterSet = setOf(splitPairFilter)
Java
Set<SplitPairFilter> filterSet = new HashSet<>(); filterSet.add(splitPairFilter);
إنشاء سمات تنسيق للقسمة:
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
: تحدِّد هذه السمة كيفية ترتيب حاويات الأنشطة بالنسبة إلى بعضها البعض، على أنّها الحاوية الأساسية أولاً.
إنشاء
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
: لضبط الحد الأقصى لنسبة العرض إلى الارتفاع (الارتفاع:العرض) في الاتجاه العمودي الذي يتم عرض تقسيمات الأنشطة له. إذا كانت نسبة العرض إلى الارتفاع للشاشة العمودية تتجاوز الحد الأقصى لنسبة العرض إلى الارتفاع، يتم إيقاف التقسيمات بغض النظر عن عرض الشاشة. ملاحظة: القيمة التلقائية هي 1.4، ما يؤدي إلى شغل الأنشطة في نافذة المهمة بأكملها في الاتجاه العمودي على معظم الأجهزة اللوحية. يمكنك أيضًا الاطّلاع علىSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
وsetMaxAspectRatioInLandscape
. تكون القيمة التلقائية للوضع الأفقي هيALWAYS_ALLOW
.setFinishPrimaryWithSecondary
: لضبط كيفية تأثير الانتهاء من جميع الأنشطة في الحاوية الثانوية على الأنشطة في الحاوية الأساسية تشير السمةNEVER
إلى أنه يجب ألا ينهي النظام الأنشطة الأساسية عند انتهاء جميع الأنشطة في الحاوية الثانوية (اطّلع على إنهاء الأنشطة).setFinishSecondaryWithPrimary
: لضبط كيفية تأثير الانتهاء من جميع الأنشطة في الحاوية الأساسية على الأنشطة في الحاوية الثانوية. تشير السمةALWAYS
إلى أنه يجب أن ينهي النظام دائمًا الأنشطة في الحاوية الثانوية عند انتهاء جميع الأنشطة في الحاوية الأساسية (اطّلع على إنهاء الأنشطة).setClearTop
: تحدِّد هذه السياسة ما إذا كانت جميع الأنشطة في الحاوية الثانوية قد اكتملت عند إطلاق نشاط جديد في الحاوية. تشير القيمة "خطأ" إلى أنّ الأنشطة الجديدة يتم تكديسها أعلى الأنشطة المتوفّرة في الحاوية الثانوية.
احصل على نسخة افتراضية من WindowManager
RuleController
، وأضِف القاعدة:Kotlin
val ruleController = RuleController.getInstance(this) ruleController.addRule(splitPairRule)
Java
RuleController ruleController = RuleController.getInstance(this); ruleController.addRule(splitPairRule);
أنشئ عنصرًا نائبًا للحاوية الثانوية عندما لا يتوفّر المحتوى:
أنشِئ عنصر
ActivityFilter
يحدِّد النشاط الذي يشارك معه العنصر النائب تقسيم نافذة المهمة:Kotlin
val placeholderActivityFilter = ActivityFilter( ComponentName(this, ListActivity::class.java), null )
Java
ActivityFilter placeholderActivityFilter = new ActivityFilter( new ComponentName(this, ListActivity.class), null );
أضِف الفلتر إلى مجموعة فلاتر:
Kotlin
val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
Java
Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>(); placeholderActivityFilterSet.add(placeholderActivityFilter);
إنشاء
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
: لضبط الحدّ الأدنى للقيمة (بالبكسل الكثافة) التي يجب أن تسمح بها القيمة الأصغر لسمتَي العرض، بغض النظر عن اتجاه الجهاز.setMaxAspectRatioInPortrait
: لضبط الحد الأقصى لنسبة العرض إلى الارتفاع (الارتفاع:العرض) في الاتجاه العمودي الذي يتم عرض تقسيمات الأنشطة له. ملاحظة: القيمة التلقائية هي 1.4، ما يؤدي إلى ملء نافذة المهمة في الاتجاه العمودي على معظم الأجهزة اللوحية. راجِع أيضًاSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
وsetMaxAspectRatioInLandscape
. تكون القيمة التلقائية للوضع الأفقي هيALWAYS_ALLOW
.setFinishPrimaryWithPlaceholder
: لضبط كيفية تأثير الانتهاء من نشاط العنصر النائب في الأنشطة في الحاوية الأساسية. يشير "دائمًا" إلى أن النظام يجب أن ينهي دائمًا الأنشطة في الحاوية الأساسية عند انتهاء العنصر النائب (راجِع إنهاء الأنشطة).setSticky
: تحدِّد هذه السياسة ما إذا كان نشاط العنصر النائب يظهر في أعلى حزمة الأنشطة على شاشات صغيرة بعد ظهور العنصر النائب للمرة الأولى في تقسيم بحدّ أدنى كافٍ للعرض.
إضافة القاعدة إلى WindowManager
RuleController
:Kotlin
ruleController.addRule(splitPlaceholderRule)
Java
ruleController.addRule(splitPlaceholderRule);
تحديد الأنشطة التي يجب ألا تكون جزءًا من التقسيم:
يمكنك إنشاء
ActivityFilter
يحدّد النشاط الذي يجب أن يشغل دائمًا منطقة عرض المهام بالكامل:Kotlin
val expandedActivityFilter = ActivityFilter( ComponentName(this, ExpandedActivity::class.java), null )
Java
ActivityFilter expandedActivityFilter = new ActivityFilter( new ComponentName(this, ExpandedActivity.class), null );
أضِف الفلتر إلى مجموعة فلاتر:
Kotlin
val expandedActivityFilterSet = setOf(expandedActivityFilter)
Java
Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>(); expandedActivityFilterSet.add(expandedActivityFilter);
إنشاء
ActivityRule
:Kotlin
val activityRule = ActivityRule.Builder(expandedActivityFilterSet) .setAlwaysExpand(true) .build()
Java
ActivityRule activityRule = new ActivityRule.Builder( expandedActivityFilterSet ).setAlwaysExpand(true) .build();
ينشئ
ActivityRule.Builder
القاعدة ويضبطها:expandedActivityFilterSet
: يحتوي على فلاتر الأنشطة التي تحدِّد وقت تطبيق القاعدة عن طريق تحديد الأنشطة التي تريد استبعادها من عمليات التقسيم.setAlwaysExpand
: لتحديد ما إذا كان يجب أن يملأ النشاط نافذة المهام بالكامل.
إضافة القاعدة إلى WindowManager
RuleController
:Kotlin
ruleController.addRule(activityRule)
Java
ruleController.addRule(activityRule);
التضمين عبر التطبيقات
في نظام التشغيل Android 13 (المستوى 33 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يمكن للتطبيقات تضمين أنشطة من تطبيقات أخرى. يتيح تضمين النشاط على مستوى عدة تطبيقات أو مع معرّف فريد دمجًا مرئيًا للأنشطة من تطبيقات Android متعدّدة. يعرض النظام نشاطًا للتطبيق المضيف ونشاطًا مضمّنًا من تطبيق آخر على الشاشة جنبًا إلى جنب أو أعلاها أو أسفلها تمامًا كما هو الحال عند تضمين نشاط تطبيق واحد.
على سبيل المثال، يمكن أن يضمِّن تطبيق "الإعدادات" نشاط أداة اختيار الخلفية من تطبيق Animation Picker:
نموذج الثقة
يمكن لعمليات المضيف التي تتضمّن أنشطة من تطبيقات أخرى إعادة تحديد طريقة عرض الأنشطة المضمّنة، بما في ذلك الحجم والموضع والاقتصاص والشفافية. يمكن للمضيفين الضارين استخدام هذه الإمكانية لتضليل المستخدمين وإنشاء تمويه النقر أو هجمات أخرى لمعالجة واجهة المستخدم.
لمنع إساءة استخدام تضمين النشاط على جميع التطبيقات، يطلب 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 عن طريق تشغيل مهمة signingReport
Gradle. ملخص الشهادة هو الملف المرجعي لخوارزمية 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.
إذا حاول أحد التطبيقات المضيفة تضمين نشاط لم يوافق على التضمين في التطبيقات المختلفة، يشغل النشاط حدود المهمة بالكامل. ونتيجةً لذلك، تحتاج التطبيقات المضيفة إلى معرفة ما إذا كانت الأنشطة المستهدفة تسمح بالتضمين عبر التطبيقات.
إذا بدأ نشاط مضمّن نشاطًا جديدًا في المهمة نفسها ولم يوافق النشاط الجديد على التضمين في تطبيقات متعددة، يشغل النشاط حدود المهمة بالكامل بدلاً من تراكب النشاط في الحاوية المضمّنة.
يمكن للتطبيق المضيف تضمين أنشطته الخاصة بدون قيود ما دام يتم بدء الأنشطة في المهمة نفسها.
أمثلة على التقسيم
التقسيم من النافذة الكاملة
ليس عليك إعادة الهيكلة. يمكنك تحديد إعدادات التقسيم بشكل ثابت أو في وقت التشغيل ثم طلب Context#startActivity()
بدون أي معلَمات إضافية.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
التقسيم تلقائيًا
عندما يتم تصميم الصفحة المقصودة لتطبيق ما لتقسيمها إلى حاويتين على شاشات كبيرة، تكون تجربة المستخدم أفضل عند إنشاء كلا النشاطين وتقديمهما في وقت واحد. ومع ذلك، قد لا يكون المحتوى متاحًا للحاوية الثانوية للقسمة حتى يتفاعل المستخدم مع النشاط في الحاوية الأساسية (على سبيل المثال، يختار المستخدم عنصرًا من قائمة التنقل). يمكن أن يملأ نشاط العنصر النائب الفراغ إلى أن يتم عرض المحتوى في الحاوية الثانوية للقسمة (راجع العناصر النائبة أعلاه).
لإنشاء تقسيم باستخدام عنصر نائب، أنشئ عنصرًا نائبًا واربطه بالنشاط الأساسي:
<SplitPlaceholderRule
window:placeholderActivityName=".PlaceholderActivity">
<ActivityFilter
window:activityName=".MainActivity"/>
</SplitPlaceholderRule>
تقسيم الروابط لصفحة في التطبيق
عندما يتلقّى التطبيق هدفًا، يمكن عرض النشاط المستهدف على أنّه الجزء الثانوي من تقسيم النشاط، مثل طلب عرض شاشة تفاصيل تتضمّن معلومات عن عنصر من قائمة. على الشاشات الصغيرة، يتم عرض التفاصيل في نافذة المهام الكاملة؛ على الأجهزة الأكبر، بجانب القائمة.
يجب توجيه طلب الإطلاق إلى النشاط الرئيسي، ويجب إطلاق نشاط التفاصيل المستهدفة في تقسيم. يختار النظام تلقائيًا العرض التقديمي الصحيح - المكدس أو جنبًا إلى جنب - بناءً على عرض الشاشة المتاح.
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>
ويمكنك الاطّلاع على سمات الإعداد أدناه.
أنشطة متعددة في الحاويات المقسّمة
يتيح تكديس أنشطة متعددة في حاوية مقسمة للمستخدمين الوصول إلى المحتوى العميق. على سبيل المثال، من خلال تقسيم القائمة مع تفاصيل، قد يحتاج المستخدم إلى الانتقال إلى قسم التفاصيل الفرعية مع إبقاء النشاط الأساسي في مكانه.
Kotlin
class DetailActivity { . . . fun onOpenSubDetail() { startActivity(Intent(this, SubDetailActivity::class.java)) } }
Java
public class DetailActivity { . . . void onOpenSubDetail() { startActivity(new Intent(this, SubDetailActivity.class)); } }
يتم وضع نشاط التفاصيل الفرعية أعلى نشاط التفاصيل، مما يخفيه:
يمكن للمستخدم بعد ذلك العودة إلى مستوى التفاصيل السابق عن طريق الانتقال مرة أخرى عبر المكدس:
يكون تكديس الأنشطة فوق بعضها البعض هو السلوك الافتراضي عند إطلاق الأنشطة من نشاط في نفس الحاوية الثانوية. إنّ الأنشطة التي يتم إطلاقها من الحاوية الأساسية داخل تقسيم نشط تنتهي أيضًا في الحاوية الثانوية أعلى حزمة الأنشطة.
الأنشطة في مهمة جديدة
عندما تبدأ الأنشطة في نافذة تقسيم المهام الأنشطة في مهمة جديدة، تكون المهمة الجديدة منفصلة عن المهمة التي تتضمن التقسيم ويتم عرض النافذة الكاملة. تعرض شاشة "العناصر الأخيرة" مهمتين: المهمة في التقسيم والمهمة الجديدة.
استبدال النشاط
يمكن استبدال الأنشطة في حزمة الحاوية الثانوية، على سبيل المثال، عندما يتمّ استخدام النشاط الأساسي للتنقّل على المستوى الأعلى وكان النشاط الثانوي وجهة محدَّدة. ينبغي أن يبدأ كل تحديد من تنقل المستوى الأعلى نشاطًا جديدًا في الحاوية الثانوية وإزالة النشاط أو الأنشطة التي كانت موجودة سابقًا.
إذا لم ينهي التطبيق النشاط في الحاوية الثانوية عند تغيير اختيار التنقّل، قد يكون الانتقال للخلف أمرًا مربكًا عند تصغير التقسيم (عندما يكون الجهاز مطويًا). على سبيل المثال، إذا كانت لديك قائمة في الجزء الأساسي والشاشتين A وB مكدسة في الجزء الثانوي، فعندما يطي المستخدم الهاتف، يكون B أعلى من A، وA في أعلى القائمة. عندما ينتقل المستخدم مرة أخرى من "ب"، تظهر "أ" بدلاً من القائمة.
يجب إزالة الشاشة "أ" من الحزمة الخلفية في هذه الحالات.
السلوك الافتراضي عند التشغيل للجانب الجانبي في حاوية جديدة عبر تقسيم حالي هو وضع الحاويات الثانوية الجديدة في الأعلى مع الاحتفاظ بالحاويات القديمة في الحزمة الخلفية. ويمكنك ضبط التقسيمات لمحو الحاويات الثانوية السابقة باستخدام 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))); } }
وبدلاً من ذلك، استخدِم النشاط الثانوي نفسه، ومن النشاط الأساسي (القائمة) يرسِل أغراضًا جديدة تفصِل إلى الحالة نفسها مع تشغيل تحديث للحالة أو واجهة المستخدم في الحاوية الثانوية.
تقسيمات متعددة
يمكن أن توفر التطبيقات التنقل العميق متعدد المستويات من خلال إطلاق أنشطة إضافية جانبًا.
عندما يبدأ نشاط في حاوية ثانوية نشاطًا جديدًا في الجانب، يتم إنشاء تقسيم جديد فوق التقسيم الحالي.
تحتوي الحزمة الخلفية على جميع الأنشطة التي تم فتحها مسبقًا، بحيث يمكن للمستخدمين الانتقال إلى قسم A/B بعد الانتهاء من C.
لإنشاء قسمة جديدة، ابدأ النشاط الجديد بجانب الحاوية الثانوية الحالية. وضح الإعدادات لكل من قسمي A/B وB/C ونشاط الإطلاق "ج" الذي عادةً ما يكون من "ب":
<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)); } }
التفاعل مع التغييرات في حالة التقسيم
يمكن أن تشتمل الأنشطة المختلفة في أحد التطبيقات على عناصر واجهة المستخدم التي تؤدي الوظيفة نفسها؛ على سبيل المثال، عنصر تحكم يفتح نافذة تحتوي على إعدادات الحساب.
إذا كان هناك نشاطان يحتويان على عنصر مشترك مشترك بين عنصر واجهة المستخدم في انقسام، سيكون عرض العنصر في كلا النشاطين مكررًا وربما مربكًا.
لمعرفة متى تكون الأنشطة مقسّمة، تحقَّق من مسار 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>
إنهاء الأنشطة
يمكن للمستخدمين إنهاء الأنشطة على أي من جانبي الشاشة عن طريق التمرير سريعًا من حافة الشاشة:
إذا تم إعداد الجهاز لاستخدام زر الرجوع بدلاً من التنقل بالإيماءات، فيتم إرسال الإدخال إلى النشاط محل التركيز، وهو النشاط الذي تم لمسه أو تشغيله مؤخرًا.
يعتمد تأثير إنهاء جميع الأنشطة في الحاوية على الحاوية المقابلة على تهيئة التقسيم.
سمات الإعداد
يمكنك تحديد سمات قاعدة زوج التقسيم لضبط مدى تأثير إنهاء جميع الأنشطة على جانب واحد من التقسيم على الأنشطة على الجانب الآخر من القسمة. السمات هي:
window:finishPrimaryWithSecondary
- كيف يؤثر الانتهاء من جميع الأنشطة في الحاوية الثانوية على الأنشطة في الحاوية الأساسيةwindow:finishSecondaryWithPrimary
- كيف يؤثر الانتهاء من جميع الأنشطة في الحاوية الأساسية في الأنشطة في الحاوية الثانوية
تشمل قيم السمات المحتملة:
always
- إنهاء الأنشطة دائمًا في الحاوية المرتبطةnever
- عدم إنهاء الأنشطة في الحاوية المرتبطة مطلقًاadjacent
- إنهاء الأنشطة في الحاوية المرتبطة عند عرض الحاوية المتجاورة، ولكن ليس عند تكديس الحاويةَين
مثلاً:
<SplitPairRule
<!-- Do not finish primary container activities when all secondary container activities finish. -->
window:finishPrimaryWithSecondary="never"
<!-- Finish secondary container activities when all primary container activities finish. -->
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
الإعدادات التلقائية
عند انتهاء جميع الأنشطة في حاوية واحدة من تقسيم، تشغل الحاوية المتبقية النافذة بأكملها:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
إنهاء الأنشطة معًا
إنهاء الأنشطة في الحاوية الأساسية تلقائيًا عند انتهاء جميع الأنشطة في الحاوية الثانوية:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
إنهاء الأنشطة في الحاوية الثانوية تلقائيًا عند انتهاء جميع الأنشطة في الحاوية الأساسية:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
إنهاء الأنشطة معًا عند انتهاء جميع الأنشطة إما في الحاوية الأساسية أو الثانوية:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
إكمال أنشطة متعددة في الحاويات
إذا تم تكديس أنشطة متعددة في حاوية مقسّمة، لن يؤدي إكمال نشاط في أسفل الحزمة إلى إنهاء الأنشطة تلقائيًا في الأعلى.
على سبيل المثال، إذا كان هناك نشاطان في الحاوية الثانوية، تكون ج:
ويتم تحديد تهيئة التقسيم من خلال ضبط النشاطين "أ" و"ب":
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
الانتهاء من النشاط العلوي يحتفظ بالتقسيم.
لا يؤدي الانتهاء من النشاط السفلي (الجذر) للحاوية الثانوية إلى إزالة الأنشطة الموجودة فوقها؛ وبالتالي، يحتفظ أيضًا بالتقسيم.
يتم أيضًا تنفيذ أي قواعد إضافية لإنهاء الأنشطة معًا، مثل إنهاء النشاط الثانوي مع الأساسي:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
وعند ضبط التقسيم على أن يتم الانتهاء من الأساسيات الأساسية والثانوية معًا:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
تغيير خصائص التقسيم عند وقت التشغيل
لا يمكن تغيير خصائص التقسيم النشط والمرئي حاليًا. يؤثر تغيير قواعد التقسيم على عمليات إطلاق الأنشطة الإضافية والحاويات الجديدة، ولكن ليس على التقسيمات الحالية والنشطة.
لتغيير خصائص التقسيمات النشطة، يمكنك إنهاء النشاط الجانبي أو الأنشطة في القسمة وبدء التنفيذ مرة أخرى باستخدام إعدادات جديدة.
استخراج نشاط من تقسيم إلى نافذة كاملة
يجب إنشاء عملية ضبط جديدة تعرض النافذة الكاملة للنشاط الجانبي، ثم إعادة تشغيل النشاط بهدف يحلّ المشكلة نفسها في المثيل نفسه.
التأكّد من توفُّر إمكانية التقسيم في وقت التشغيل
تتوفّر ميزة تضمين الأنشطة على أجهزة 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 (المصنّعين الأصليين للأجهزة أو المصنّعين الأصليين للأجهزة) تنفيذ تضمين الأنشطة كوظيفة لنظام الجهاز. يحدّد النظام قواعد التقسيم للتطبيقات المتعددة الأنشطة، ما يؤدي إلى تجاوز سلوك إنشاء النوافذ. يؤدي إلغاء النظام إلى فرض وضع تضمين النشاط الذي يحدّده النظام على التطبيقات المتعددة الأنشطة.
يمكن أن يؤدي تضمين أنشطة النظام إلى تحسين عرض التطبيق من خلال تنسيقات متعددة الأجزاء، مثل تفاصيل القائمة، بدون أي تغييرات في التطبيق. ومع ذلك، قد يؤدي تضمين نشاط النظام أيضًا إلى تخطيطات غير صحيحة للتطبيق أو أخطاء أو تعارضات مع تضمين الأنشطة التي ينفّذها التطبيق.
يمكن لتطبيقك منع تضمين نشاط النظام أو السماح به من خلال ضبط خاصية في ملف بيان التطبيق، على سبيل المثال:
<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>
يتم تحديد اسم الخاصية في الكائن WindowProperties في Jetpack WindowManager.
يمكنك ضبط القيمة على false
إذا كان تطبيقك ينفِّذ تضمين الأنشطة، أو إذا كنت تريد منع النظام من تطبيق قواعد تضمين الأنشطة على تطبيقك. اضبط القيمة على true
للسماح للنظام بتطبيق تضمين النشاط المحدّد من خلال النظام على تطبيقك.
القيود والقيود والتنبيهات
- يمكن فقط للتطبيق المضيف للمهمة، الذي تم تحديده كمالك للنشاط الجذري في المهمة، تنظيم الأنشطة الأخرى وتضمينها في المهمة. وإذا كانت الأنشطة التي تتيح التضمين والتقسيم تعمل في مهمة تنتمي إلى تطبيق مختلف، لن يعمل التضمين والتقسيم مع تلك الأنشطة.
- يمكن تنظيم الأنشطة في مهمة واحدة فقط. يؤدي بدء نشاط في مهمة جديدة دائمًا إلى وضعه في نافذة موسعة جديدة خارج أي تقسيمات حالية.
- يمكن تنظيم الأنشطة في نفس العملية فقط وتقسيمها. لا تعرض ميزة معاودة الاتصال
SplitInfo
إلا الأنشطة التي تنتمي إلى العملية نفسها، بما أنّه لا تتوفّر طريقة لمعرفة الأنشطة في عمليات مختلفة. - لا ينطبق كل زوج أو قاعدة نشاط فردية إلا على عمليات إطلاق النشاط التي تحدث بعد تسجيل القاعدة. ولا توجد طريقة حاليًا لتعديل التقسيمات الحالية أو خصائصها المرئية.
- يجب أن تتطابق تهيئة فلتر الزوج المقسّم مع الأغراض المستخدمة عند إطلاق الأنشطة بالكامل. وتحدث المطابقة عند بدء نشاط جديد من عملية التطبيق، لذلك قد لا يعرف أسماء المكوّنات التي يتم حلّها لاحقًا في عملية النظام عند استخدام الأهداف الضمنية. وإذا لم يكن اسم المكوّن معروفًا في وقت الإطلاق، يمكن استخدام حرف بدل بدلاً منه ("*/*") ويمكن تطبيق الفلترة استنادًا إلى الإجراء المقصود.
- ولا تتوفّر حاليًا طريقة لنقل الأنشطة بين الحاويات أو داخل وخارج التقسيمات بعد إنشائها. لا يتم إنشاء التقسيمات إلا بواسطة مكتبة WindowManager عند إطلاق أنشطة جديدة ذات قواعد مطابقة، ويتم إتلاف الأقسام عند الانتهاء من آخر نشاط في حاوية مقسمة.
- يمكن إعادة تشغيل الأنشطة بعد تغيير الإعدادات، لذلك عند إنشاء عملية تقسيم أو إزالتها وتغيير حدود النشاط، يمكن أن يخضع النشاط للتدمير الكامل للمثيلات السابقة وإنشاء المثيل السابق. نتيجة لذلك، يجب أن يكون مطورو التطبيقات حريصين بشأن أشياء مثل إطلاق أنشطة جديدة من عمليات معاودة الاتصال في مراحل النشاط.
- يجب أن تتضمّن الأجهزة واجهة إضافات النوافذ لإتاحة تضمين النشاط. تشتمل تقريبًا جميع الأجهزة ذات الشاشات الكبيرة التي تعمل بنظام التشغيل Android 12L (المستوى 32 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث على الواجهة. ومع ذلك، لا تتضمّن بعض الأجهزة ذات الشاشات الكبيرة غير قادرة على تشغيل أنشطة متعدّدة واجهة إضافات النوافذ. إذا كان الجهاز ذو الشاشة الكبيرة لا يتيح وضع النوافذ المتعددة، قد لا يتيح تضمين النشاط.
مراجع إضافية
- درس تطبيقي حول الترميز: إنشاء تنسيق قائمة بالتفاصيل يتضمّن النشاط
- المسار التعليمي - تضمين الأنشطة