تعمل ميزة "تضمين الأنشطة" على تحسين التطبيقات على الأجهزة ذات الشاشات الكبيرة من خلال تقسيم نافذة مهام التطبيق بين نشاطَين أو نسختَين من النشاط نفسه.
إذا كان تطبيقك يتألف من أنشطة متعددة، تتيح لك ميزة "تضمين الأنشطة" تقديم تجربة محسّنة للمستخدمين على الأجهزة اللوحية والأجهزة القابلة للطي وأجهزة ChromeOS.
لا تتطلّب عملية تضمين الأنشطة إعادة صياغة الرموز البرمجية. يمكنك تحديد كيفية برمجة تطبيقك لعرض الأنشطة، سواء بجانب بعضها أو مُكدّسة، وذلك من خلال إنشاء ملف إعدادات XML أو من خلال إجراء طلبات بيانات من واجهة برمجة التطبيقات Jetpack WindowManager.
يتم تلقائيًا الاحتفاظ بتوافق التطبيق مع الشاشات الصغيرة. عندما يكون تطبيقك على جهاز مزوّد بشاشة صغيرة، يتم تجميع الأنشطة فوق بعضها. على الشاشات الكبيرة، يتم عرض الأنشطة جنبًا إلى جنب. يحدِّد النظام أسلوب عرض التقارير استنادًا إلى الإعدادات التي أنشأتها، ولا يتطلّب ذلك استخدام منطق لتشعُّب التقارير.
تتيح ميزة "تضمين الأنشطة" تغيير اتجاه الجهاز، وتعمل بسلاسة على الأجهزة القابلة للطي، كما تتيح تجميع الأنشطة وتفكيكها أثناء طي الجهاز وفتحه.
تتوفّر ميزة تضمين الأنشطة على معظم الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار 12L من نظام التشغيل Android (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأحدث.
نافذة المهمة المُقسّمة
تؤدي عملية تضمين النشاط إلى تقسيم نافذة مهمة التطبيق إلى حاويتَين: حاوية أساسية و حاوية ثانوية. تحتوي الحاويات على الأنشطة التي يتم إطلاقها من النشاط الرئيسي أو من الأنشطة الأخرى المتوفّرة في الحاويات.
يتم تجميع الأنشطة في الحاوية الثانوية عند إطلاقها، ويتم تجميع الحاوية الثانوية فوق الحاوية الأساسية على الشاشات الصغيرة، لذا يكون تجميع الأنشطة والتنقّل للخلف متوافقًا مع ترتيب الأنشطة المضمّنة في تطبيقك.
تتيح لك ميزة "تضمين الأنشطة" عرض الأنشطة بعدة طرق. يمكن لتطبيقك تقسيم نافذة المهام من خلال تشغيل نشاطَين جنبًا إلى جنب في الوقت نفسه:
أو يمكن أن يؤدي النشاط الذي يشغل نافذة المهام بأكملها إلى إنشاء تقسيم من خلال بدء نشاط جديد بجانب:
يمكن للأنشطة التي تم تقسيمها ومشاركة نافذة مهمة فيها تشغيل أنشطة أخرى بالطرق التالية:
على الجانب أعلى نشاط آخر:
إلى الجانب، وحرِّك القسم إلى الجانب، ما يؤدي إلى إخفاء النشاط الأساسي السابق:
ابدأ نشاطًا في مكانه في أعلى الصفحة، أي في مجموعة الأنشطة نفسها:
افتح نافذة نشاط كاملة في المهمة نفسها:
الانتقال إلى الصفحة السابقة
يمكن أن تتضمّن الأنواع المختلفة من التطبيقات قواعد مختلفة للتنقّل للخلف في حالة نافذة مهمة مُقسّمة استنادًا إلى التبعيات بين الأنشطة أو كيفية تنشيط المستخدِمين لحدث الرجوع، على سبيل المثال:
- الظهور معًا: إذا كانت الأنشطة مرتبطة ببعضها، ولا يجب عرض أحدها بدون الآخر، يمكن ضبط التنقّل للخلف لإكمال كليهما.
- الاعتماد على الذات: إذا كانت الأنشطة مستقلة تمامًا، لا يؤثّر التنقّل للخلف في أحد الأنشطة في حالة نشاط آخر في نافذة المهام.
يتم إرسال حدث الرجوع إلى آخر نشاط تم التركيز عليه عند استخدام زر التنقّل.
للتنقّل بالاستناد إلى الإيماءات:
الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) والإصدارات الأقدم: يتم إرسال حدث الرجوع إلى النشاط الذي حدثت فيه الإيماءة. عندما يمرر المستخدمون سريعًا من الجانب الأيسر للشاشة، يتم إرسال حدث الرجوع إلى النشاط في اللوحة اليسرى من النافذة المُقسّمة. عندما يمرر المستخدمون سريعًا من الجانب الأيمن من الشاشة، يتم إرسال حدث الرجوع إلى النشاط في اللوحة اليمنى.
الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات) والإصدارات الأحدث
عند التعامل مع أنشطة متعددة من التطبيق نفسه، تؤدي الإيماءة إلى إنهاء النشاط العلوي بغض النظر عن اتجاه التمرير السريع، ما يقدّم تجربة أكثر اتساقًا.
في السيناريوهات التي تتضمّن نشاطَين من تطبيقَين مختلفَين (تداخل)، يتم توجيه حدث الرجوع إلى النشاط الأخير الذي كان في المقدّمة، بما يتوافق مع سلوك التنقّل باستخدام الأزرار.
تنسيق متعدد الأقسام
تتيح لك مكتبة 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 يُرتِّب الأنشطة في اللوحة الثانوية فوق الأنشطة في اللوحة الأساسية بترتيب أبجدي عكسي.
أنشطة متعددة في اللوحة الثانوية
يبدأ النشاط "ب" النشاط "ج" بدون أي علامات إضافية للنوايا:
ما أدّى إلى ترتيب الأنشطة التالي في المهمة نفسها:
وبالتالي، في نافذة مهام أصغر حجمًا، يتم تصغير التطبيق إلى نشاط واحد مع C في أعلى الحزمة:
يؤدي التنقّل للخلف في النافذة الأصغر إلى التنقّل بين الأنشطة المكدّسة فوق بعضها.
في حال استعادة إعدادات نافذة المهام إلى حجم أكبر يمكنه استيعاب عدة أقسام، يتم عرض الأنشطة جنبًا إلى جنب مرة أخرى.
التقسيمات المكدّسة
يبدأ النشاط "ب" النشاط "ج" على الجانب وينقل الجزء المُقسَّم إلى الجانب:
والنتيجة هي الترتيب z التالي للأنشطة في المهمة نفسها:
في نافذة مهام أصغر حجمًا، يتم تصغير التطبيق إلى نشاط واحد مع C فيверху:
الاتجاه العمودي الثابت
يتيح إعداد البيان android:screenOrientation للتطبيقات تقييد الأنشطة بالاتجاه العمودي أو الأفقي. لتحسين تجربة المستخدم على الأجهزة ذات الشاشات الكبيرة، مثل الأجهزة اللوحية والأجهزة القابلة للطي، يمكن لمصنعي الأجهزة (OEM) تجاهل طلبات اتجاه الشاشة وعرض التطبيق في وضع شاشة عريضة عمودية على الشاشات الأفقية أو وضع شاشة عريضة أفقية على الشاشات الرأسية.
وبالمثل، عند تفعيل ميزة تضمين الأنشطة، يمكن لمصنّعي المعدّات الأصلية تخصيص الأجهزة لعرض أنشطة في وضع مُعدّ للعرض على شاشة عريضة في الوضع العمودي على الشاشات الكبيرة (يجب أن يكون العرض أكبر من أو يساوي 600dp). عندما يشغِّل نشاط في الوضع العمودي الثابت نشاطًا ثانيًا، يمكن للجهاز عرض النشاطَين جنبًا إلى جنب في شاشة من قسمَين.
أضِف دائمًا السمة android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
إلى ملف بيان تطبيقك لإعلام الأجهزة بأنّ تطبيقك يتيح ميزة ملفّات النشاط المضمّنة (راجِع القسم إعداد "التقسيم"
). يمكن للأجهزة المخصّصة من المصنّعين الأصليين للأجهزة بعد ذلك تحديد ما إذا كان سيتم عرض الأنشطة في وضع "شاشة عريضة أفقيًا"
أو في وضع "شاشة عريضة عموديًا".
إعدادات التقسيم
تعمل قواعد التقسيم على ضبط عمليات تقسيم الأنشطة. يمكنك تحديد قواعد التقسيم في ملف إعدادات XML أو من خلال إجراء طلبات WindowManager API في Jetpack.
وفي كلتا الحالتَين، يجب أن يصل تطبيقك إلى مكتبة WindowManager وأن يُعلم النظام بأنّ التطبيق قد نفَّذ عملية تضمين النشاط.
اتّبِع الخطوات التالية:
أضِف أحدث مكتبة WindowManager التي يعتمد عليها تطبيقك إلى ملف
build.gradle
على مستوى الوحدة في تطبيقك، على سبيل المثال:implementation 'androidx.window:window:1.1.0-beta02'
توفّر مكتبة WindowManager جميع المكوّنات المطلوبة لدمج النشاط.
أطلِع النظام على أنّ تطبيقك قد نفَّذ عملية تضمين النشاط.
أضِف السمة
android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
إلى العنصر <application> في ملف بيان التطبيق، واضبط القيمة على true، على سبيل المثال:<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <property android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED" android:value="true" /> </application> </manifest>
في الإصدار 1.1.0-alpha06 من WindowManager والإصدارات الأحدث، يتم إيقاف عمليات تقسيم تضمين النشاط ما لم تتم إضافة السمة إلى البيان وضبطها على true.
ويستخدم مصنعو الأجهزة هذا الإعداد لتفعيل إمكانات مخصّصة ل التطبيقات التي تتيح تضمين الأنشطة. على سبيل المثال، يمكن للأجهزة عرض نشاط في وضع مربّع على شاشة أفقية لتوجيه النشاط إلى وضع عمودي عند الانتقال إلى تنسيق نوافذ مزدوجة عند بدء نشاط ثانٍ (راجِع الوضع الثابت بالاتجاه العمودي).
إعدادات XML
لإنشاء عملية تضمين نشاط مستندة إلى XML، أكمِل الخطوات التالية:
أنشئ ملف موارد 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()
: لضبط الحد الأدنى للقيمة (بالوحدة dp) التي يجب أن يمتلكها أصغر سمة من سمتَي الشاشة لتفعيل القسمة بغض النظر عن اتجاه الجهاز -
setMaxAspectRatioInPortrait()
: لضبط الحد الأقصى لنسبة قياس الشاشة (الارتفاع إلى العرض) في الوضع العمودي الذي يتم عرض تقسيمات النشاط عليه. إذا كانت نسبة العرض إلى الارتفاع لشاشة في الوضع العمودي تتعدى الحد الأقصى لنسبة العرض إلى الارتفاع، يتم إيقاف عمليات التقسيم بغض النظر عن عرض الشاشة. ملاحظة: القيمة التلقائية هي 1.4، ما يؤدي إلى أن تشغل الأنشطة نافذة المهام بالكامل في الوضع عمودي على معظم الأجهزة اللوحية. يمكنك أيضًا الاطّلاع علىSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
وsetMaxAspectRatioInLandscape()
. القيمة التلقائية لاتجاه اللوحة هيALWAYS_ALLOW
. setFinishPrimaryWithSecondary()
: لضبط كيفية تأثير إنهاء كل الأنشطة في الحاوية الثانوية في الأنشطة في الحاوية الأساسية. يشير الرمزNEVER
إلى أنّه يجب ألا يُنهي النظام الأنشطة الأساسية عند انتهاء جميع الأنشطة في الحاوية الثانوية (راجِع إنهاء الأنشطة).setFinishSecondaryWithPrimary()
: لضبط كيفية تأثير إنهاء كل الأنشطة في الحاوية الأساسية في الأنشطة في الحاوية الثانوية. يشير الرمزALWAYS
إلى أنّه على النظام دائمًا إنهاء الأنشطة في الحاوية الثانوية عند انتهاء جميع الأنشطة في الحاوية الأساسية (راجِع إنهاء الأنشطة).-
setClearTop()
: يحدِّد ما إذا كان سيتم إنهاء جميع الأنشطة في الحاوية الثانوية عند بدء نشاط جديد في الحاوية. تُحدِّد القيمةfalse
أنّ الأنشطة الجديدة يتم تجميعها فوق الأنشطة المتوفّرة حاليًا في الحاوية الثانوية.
احصل على مثيل فريد من 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()
: تُستخدَم لضبط الحد الأدنى للقيمة (بوحدة dp) التي يجب أن يمتلكها أصغر سمة من سمتَي الشاشة للسماح بالتقسيم بغض النظر عن اتجاه الجهاز. -
setMaxAspectRatioInPortrait()
: يضبط الحد الأقصى لنسبة العرض إلى الارتفاع للشاشة (الارتفاع:العرض) في الوضع عمودي الذي يتم عرض تقسيمات الأنشطة فيه. ملاحظة: القيمة التلقائية هي 1.4، ما يؤدي إلى ملء الأنشطة لشاشة المهام في الوضع العمودي على معظم الأجهزة اللوحية. يمكنك أيضًا الاطّلاع علىSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
وsetMaxAspectRatioInLandscape()
. القيمة التلقائية للوضع الأفقي هيALWAYS_ALLOW
. -
setFinishPrimaryWithPlaceholder()
: يضبط كيفية تأثُّر الأنشطة في الحاوية الأساسية عند إنهاء النشاط النائب. يشير ALWAYS إلى أنّه يجب على النظام دائمًا إنهاء الأنشطة في الحاوية الأساسية عند انتهاء العنصر النائب (راجِع إنهاء الأنشطة). -
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);
التضمين في تطبيقات متعددة
في الإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يمكن للتطبيقات تضمين أنشطة من التطبيقات الأخرى. يتيح تضمين الأنشطة على مستوى التطبيقات أو على مستوى UID دمج الأنشطة المرئية من تطبيقات Android متعددة. يعرض النظام نشاطًا للتطبيق المضيف ونشاطًا مضمّنًا من تطبيق آخر على الشاشة جنبًا إلى جنب أو في أعلى الشاشة أو أسفلها تمامًا كما هو الحال في تضمين نشاط تطبيق واحد.
على سبيل المثال، يمكن أن يدمج تطبيق "الإعدادات" نشاط أداة اختيار الخلفية من تطبيق WallpaperPicker:
نموذج الثقة
يمكن لعمليات المضيف التي تضمّن أنشطة من تطبيقات أخرى إعادة تحديد طريقة عرض الأنشطة المضمّنة، بما في ذلك الحجم والوضع والاقتصاص والشفافية. يمكن للمضيفين الضارّين استخدام هذه الميزة لتضليل المستخدمين و إنشاء خدعة النقر أو هجمات أخرى تستهدف واجهة المستخدم.
لمنع إساءة استخدام عملية تضمين الأنشطة على مستوى التطبيقات المختلفة، يطلب نظام التشغيل Android من التطبيقات تفعيل هذه الميزة للسماح بتضمين أنشطتها. يمكن للتطبيقات تصنيف المضيفين على أنّهم موثوق بهم أو غير موثوق بهم.
المضيفون الموثوق بهم
للسماح للتطبيقات الأخرى بتضمين الأنشطة من تطبيقك والتحكّم بشكل كامل في عرضها، حدِّد شهادة SHA-256 للتطبيق المضيف
في سمة android:knownActivityEmbeddingCerts
لعنصرَي
<activity>
أو <application>
في ملف بيان تطبيقك.
اضبط قيمة android:knownActivityEmbeddingCerts
إما على هيئة سلسلة:
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
... />
أو، لتحديد شهادات متعددة، مصفوفة من السلاسل:
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
... />
التي تشير إلى مورد مثل ما يلي:
<resources>
<string-array name="known_host_certificate_digests">
<item>cert1</item>
<item>cert2</item>
...
</string-array>
</resources>
يمكن لمالكي التطبيقات الحصول على خلاصة شهادة SHA من خلال تشغيل مهمة Gradle
signingReport
. خلاصة الشهادة هي الملف المرجعي لشهادة SHA-256 بدون
الفاصلات المنفصلة. لمزيد من المعلومات، يُرجى الاطّلاع على تشغيل تقرير التوقيع و
مصادقة العميل.
المضيفون غير الموثوق بهم
للسماح لأي تطبيق بتضمين أنشطة تطبيقك والتحكّم في عرضها،
حدِّد سمة android:allowUntrustedActivityEmbedding
في عناصر
<activity>
أو <application>
في ملف بيان التطبيق، على سبيل المثال:
<activity
android:name=".MyEmbeddableActivity"
android:allowUntrustedActivityEmbedding="true"
... />
القيمة التلقائية للسمة هي false، ما يمنع تضمين النشاط على مستوى التطبيقات المختلفة.
المصادقة المخصّصة
للحدّ من مخاطر تضمين الأنشطة غير الموثوق بها، أنشئ أسلوب مصادقة مخصّصًا يقيس هوية المضيف. إذا كنت تعرف شهادات المضيف، استخدِم مكتبة androidx.security.app.authenticator
للتحقّق من الهوية. إذا سجّل المضيف الدخول بعد تضمين نشاطك، يمكنك
عرض المحتوى الفعلي. وإذا لم يكن الأمر كذلك، يمكنك إبلاغ المستخدم بأنّه
لم يُسمح بتنفيذ الإجراء وحظر المحتوى.
استخدِم طريقة ActivityEmbeddingController#isActivityEmbedded()
من مكتبة
Jetpack WindowManager للتحقّق مما إذا كان المضيف يُضمِّن
نشاطك، على سبيل المثال:
Kotlin
fun isActivityEmbedded(activity: Activity): Boolean { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity) }
Java
boolean isActivityEmbedded(Activity activity) { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity); }
الحد الأدنى للحجم
يطبّق نظام Android الحد الأدنى للارتفاع والعرض المحدّدَين في عنصر
بيان التطبيق <layout>
على الأنشطة المضمّنة. إذا لم يحدد أحد التطبيقات
الحد الأدنى للارتفاع والعرض، يتم تطبيق القيم التلقائية للنظام
(sw220dp
).
إذا حاول المضيف تغيير حجم الحاوية المضمّنة إلى حجم أصغر من الحد الأدنى، تتم توسيع الحاوية المضمّنة لتشغل حدود المهمة بالكامل.
<activity-alias>
لكي تعمل عملية تضمين الأنشطة الموثوق بها أو غير الموثوق بها مع عنصر
<activity-alias>
، يجب تطبيق android:knownActivityEmbeddingCerts
أو
android:allowUntrustedActivityEmbedding
على النشاط المستهدَف
بدلاً من الاسم المعرِّف. تستند السياسة التي تتحقّق من الأمان على خادم النظام
إلى العلامات التي تم ضبطها على الهدف، وليس العنوان البديل.
التطبيق المضيف
تنفِّذ التطبيقات المستضيفة عملية تضمين الأنشطة على مستوى التطبيقات بالطريقة نفسها التي تتم بها
عملية تضمين الأنشطة على مستوى تطبيق واحد. تحدِّد عناصر SplitPairRule
و
SplitPairFilter
أو ActivityRule
وActivityFilter
الأنشطة المضمّنة وتقسيمات نافذة المهام. يتم تعريف قواعد التقسيم
بشكل ثابت في ملف XML أو أثناء التشغيل باستخدام طلبات بيانات واجهة برمجة التطبيقات لنظام WindowManager في Jetpack.
إذا حاول تطبيق مضيف تضمين نشاط لم يوافق على التضمين في تطبيقات متعددة، سيشغل النشاط حدود المهمة بالكامل. نتيجةً لذلك، تحتاج التطبيقات المُضيفة إلى معرفة ما إذا كانت الأنشطة المستهدَفة تسمح بعمليات التكامل بين التطبيقات.
إذا بدأ نشاط مضمّن نشاطًا جديدًا في المهمة نفسها ولم يوافق النشاط الجديد على التضمين على مستوى التطبيقات، سيشغل النشاط حدود المهمة بالكامل بدلاً من تداخل النشاط في الحاوية المضمّنة.
يمكن للتطبيق المضيف تضمين أنشطته بدون قيود طالما أنّه يتم بدء الأنشطة في المهمة نفسها.
أمثلة على التقسيم
التقسيم من نافذة ملء الشاشة
ولا يلزم إجراء إعادة صياغة. يمكنك تحديد إعدادات التقسيم
بشكل ثابت أو أثناء التشغيل، ثم استدعاء 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)); } }
يتم وضع النشاط الفرعي التفصيلي فوق النشاط التفصيلي، ما يؤدي إلى إخفائه:
يمكن للمستخدم بعد ذلك الرجوع إلى مستوى التفاصيل السابق من خلال الانتقال مجددًا من خلال الحزمة:
إنّ تجميع الأنشطة فوق بعضها هو السلوك التلقائي عند بدء الأنشطة من نشاط في الحاوية الثانوية نفسها. تنتهي الأنشطة التي يتم إطلاقها من الحاوية الأساسية ضمن قسم نشط أيضًا في الحاوية الثانوية في أعلى مجموعة الأنشطة.
الأنشطة في مهمة جديدة
عندما تبدأ الأنشطة في نافذة مهمة مُقسَّمة أنشطة في مهمة جديدة، تكون المَهمّة الجديدة منفصلة عن المهمة التي تتضمّن التقسيم ويتم عرضها في نافذة كاملة. تعرض شاشة "المهام الأخيرة" مهمتَين: المهمة في القسم المُقسَّم والمهمّة الجديدة.
استبدال النشاط
يمكن استبدال الأنشطة في حزمة الحاوية الثانوية، على سبيل المثال، عندما يتم استخدام النشاط الأساسي للتنقّل في المستوى الأعلى والنشاط الثانوي هو وجهة محدّدة. من المفترض أن يؤدي كل اختيار من شريط التنقّل في المستوى الأعلى إلى بدء نشاط جديد في الحاوية الثانوية وإزالة النشاط أو الأنشطة التي كانت موجودة سابقًا.
إذا لم يُنهِ التطبيق النشاط في الحاوية الثانوية عند تغيير اختيار التنقّل، قد يكون التنقّل للخلف مربكًا عند تصغير الشاشة المُقسّمة (عند طيّ الجهاز). على سبيل المثال، إذا كانت لديك قائمة في اللوحة الأساسية والشاشتان "أ" و"ب" مُكدَّستَين في اللوحة الثانوية، عندما يقلب المستخدم الهاتف، تظهر الشاشة "ب" فوق الشاشة "أ"، وتظهر الشاشة "أ" فوق القائمة. عندما يعود المستخدم من "ب"، تظهر "أ" بدلاً من القائمة.
يجب إزالة الشاشة "أ" من الحزمة الخلفية في هذه الحالات.
السلوك التلقائي عند الإطلاق على الجانب في حاوية جديدة فوق 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))); } }
بدلاً من ذلك، يمكنك استخدام النشاط الثانوي نفسه، ومن النشاط الأساسي (القائمة)، يمكنك إرسال نوايا جديدة تؤدي إلى العنصر نفسه، ولكن تؤدي إلى تعديل حالة أو واجهة المستخدم في الحاوية الثانوية.
تقسيمات متعددة
يمكن للتطبيقات توفير تنقّل عميق على عدّة مستويات من خلال إطلاق أنشطة إضافية على الجانب.
عندما يطلق نشاط في حاوية ثانوية نشاطًا جديدًا على الجانب، يتم إنشاء فسحة إعلانية جديدة فوق الفسحة الإعلانية الحالية.
تحتوي حزمة التطبيقات التي تمّ إغلاقها مؤخرًا على جميع الأنشطة التي تمّ فتحها سابقًا، لكي يتمكّن المستخدمون من الانتقال إلى المجموعة التجريبية أ/ب بعد الانتهاء من المجموعة ج.
لإنشاء قسم جديد، اطلق النشاط الجديد على جانب الحاوية الثانوية الحالية. حدِّد الإعدادات لكلٍّ من القسمَين A/B وB/C وأطلق النشاط C بشكلٍ طبيعي من B:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
<SplitPairFilter
window:primaryActivityName=".B"
window:secondaryActivityName=".C"/>
</SplitPairRule>
Kotlin
class B { . . . fun onOpenC() { startActivity(Intent(this, C::class.java)) } }
Java
public class B { . . . void onOpenC() { startActivity(new Intent(this, C.class)); } }
التفاعل مع تغييرات حالة الانقسام
يمكن أن تتضمّن الأنشطة المختلفة في التطبيق عناصر واجهة مستخدم تؤدي وظيفتها نفسها، على سبيل المثال، عنصر تحكّم يفتح نافذة تحتوي على إعدادات حساب.
إذا كان هناك نشاطان يتشاركان عنصر واجهة مستخدم في قسم مُقسَّم، قد يؤدي عرض العنصر في كلا النشاطَين إلى تكرار المعلومات وربما إلى حدوث التباس.
لمعرفة الحالات التي تكون فيها الأنشطة في عملية تقسيم، تحقّق من عملية
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>
إنهاء الأنشطة
يمكن للمستخدمين إنهاء الأنشطة على أي من جانبَي القسم من خلال التمرير سريعًا من حافة الشاشة:
إذا تم ضبط الجهاز لاستخدام زر الرجوع بدلاً من التنقّل بالإيماءات، يتم إرسال الإدخال إلى النشاط الذي تم التركيز عليه، أي النشاط الذي تم لمسه أو بدؤه مؤخرًا.
يعتمد تأثير إنهاء جميع الأنشطة في حاوية على الحاوية المقابلة على إعدادات التقسيم.
سمات الضبط
يمكنك تحديد سمات قاعدة أزواج التقسيم لضبط كيفية تأثير إنهاء كل ال activities على جانب واحد من التقسيم في الأنشطة على الجانب الآخر من التقسيم. السمات هي:
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 15 (المستوى 35 من واجهة برمجة التطبيقات) والإصدارات الأحدث مع الإصدار 1.4 من WindowManager في Jetpack والإصدارات الأحدث، ويقدّم هذان الإصداران ميزات ديناميكية تتيح إمكانية ضبط عمليات دمج الأنشطة، بما في ذلك:
- توسيع اللوحة: يتيح المقسم التفاعلي القابل للسحب للمستخدمين تغيير حجم اللوحة في عرض مقسم.
- تثبيت حِزم الأنشطة: يمكن للمستخدمين تثبيت المحتوى في حاوية واحدة وفصل التنقّل في الحاوية عن التنقّل في الحاوية الأخرى.
- تعتيم مربّع الحوار في وضع ملء الشاشة: عند عرض مربّع حوار، يمكن للتطبيقات تحديد ما إذا كان سيتم تعتيم نافذة المهام بأكملها أو الحاوية التي فتحت المربّع فقط.
توسيع اللوحة
يتيح توسيع اللوحة للمستخدمين تعديل مقدار مساحة الشاشة المخصّصة للنشاطَين في تنسيق اللوحة المزدوجة.
لتخصيص مظهر مقسم النافذة وضبط النطاق القابل للسحب للمقسّم، اتّبِع الخطوات التالية:
إنشاء مثيل من
DividerAttributes
تخصيص سمات المقسم:
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();
تثبيت مجموعة الأنشطة
يتيح تثبيت حِزم الأنشطة للمستخدمين تثبيت إحدى النوافذ المُقسَّمة كي يظلّ النشاط كما هو بينما ينتقل المستخدمون داخل النافذة الأخرى. توفّر ميزة تثبيت "تصنيف الأنشطة" تجربة محسّنة لتعدد المهام.
لتفعيل تثبيت حِزم الأنشطة في تطبيقك، اتّبِع الخطوات التالية:
أضِف زرًا إلى ملف تنسيق النشاط الذي تريد تثبيته، مثلاً، النشاط التفصيلي لتنسيق قائمة التفاصيل:
<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>
في طريقة
onCreate()
للنشاط، اضبط أداة معالجة onclick على الزر:Kotlin
pinButton = findViewById(R.id.pinButton) pinButton.setOnClickListener { val splitAttributes: SplitAttributes = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build() val pinSplitRule = SplitPinRule.Builder() .setSticky(true) .setDefaultSplitAttributes(splitAttributes) .build() SplitController.getInstance(applicationContext).pinTopActivityStack(taskId, pinSplitRule) }
Java
Button pinButton = findViewById(R.id.pinButton); pinButton.setOnClickListener( (view) => { SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build(); SplitPinRule pinSplitRule = new SplitPinRule.Builder() .setSticky(true) .setDefaultSplitAttributes(splitAttributes) .build(); SplitController.getInstance(getApplicationContext()).pinTopActivityStack(getTaskId(), pinSplitRule); });
تعتيم مربّع الحوار بملء الشاشة
تعمل الأنشطة عادةً على تعتيم شاشاتها لجذب الانتباه إلى مربّع حوار. في حالة تضمين النشاط، يجب أن يتم إطفاء الإضاءة في كلتا لوحتَي العرض المكوّنة من لوحتَين، وليس فقط في اللوحة التي تحتوي على النشاط الذي فتح مربّع الحوار، وذلك لتوفير تجربة موحّدة لواجهة المستخدم.
باستخدام الإصدار 1.4 من WindowManager والإصدارات الأحدث، يتم تعتيم نافذة التطبيق بالكامل تلقائيًا عند فتح
مربّع حوار (راجِع EmbeddingConfiguration.DimAreaBehavior.ON_TASK
).
لتعتيم حاوية النشاط الذي فتح مربّع الحوار فقط، استخدِم
EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK
.
استخراج نشاط من نافذة مُقسَّمة إلى نافذة ملء الشاشة
أنشئ إعدادًا جديدًا يعرض نافذة النشاط الجانبي بالكامل، ثم أعِد تشغيل النشاط باستخدام نية تؤدي إلى المثيل نفسه.
التحقّق من توفّر ميزة "التقسيم" أثناء التشغيل
تتوفّر ميزة تضمين الأنشطة على نظام التشغيل Android 12L (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأحدث، ولكنها متوفرة أيضًا على بعض الأجهزة التي تعمل بإصدارات أقدم من نظام التشغيل. للتحقّق من توفّر الميزة أثناء
التشغيل، استخدِم السمة
SplitController.splitSupportStatus
أو الطريقة
SplitController.getSplitSupportStatus()
:
Kotlin
if (SplitController.getInstance(this).splitSupportStatus == SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { // Device supports split activity features. }
Java
if (SplitController.getInstance(this).getSplitSupportStatus() == SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { // Device supports split activity features. }
إذا لم تكن عمليات التقسيم متاحة، يتم تشغيل الأنشطة على قمة ملف activity المجمّع (باتّباع نموذج تضمين غير الأنشطة).
منع تجاوز إعدادات النظام
يمكن لصنّاع أجهزة Android (المصنّعون الأصليون للأجهزة) تنفيذ ميزة تضمين الأنشطة كجزء من نظام الجهاز. يحدِّد النظام قواعد تقسيم التطبيقات المتعدّدة الأنشطة، ما يؤدي إلى إلغاء سلوك استخدام النوافذ للتطبيقات. يفرض إلغاء الإعدادات من النظام على التطبيقات التي تتضمّن أنشطة متعددة استخدام وضع تضمين الأنشطة الذي يحدّده النظام.
يمكن أن يؤدي تضمين أنشطة النظام إلى تحسين عرض التطبيق من خلال تصاميم صفحات متعددة ، مثل قائمة التفاصيل، بدون إجراء أي تغييرات على التطبيق. ومع ذلك، قد يؤدي تضمين أنشطة النظام أيضًا إلى حدوث أخطاء أو تصاميم تطبيقات غير صحيحة أو تعارض مع عمليات تضمين الأنشطة التي ينفّذها التطبيق.
يمكن لتطبيقك منع تضمين نشاط النظام أو السماح به من خلال ضبط سمة في ملف بيان التطبيق، على سبيل المثال:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<property
android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
android:value="true|false" />
</application>
</manifest>
يتم تحديد اسم السمة في كائن Jetpack WindowManager WindowProperties
. اضبط القيمة على false
إذا كان تطبيقك ينفّذ عملية تضمين النشاط، أو
إذا كنت تريد منع النظام من تطبيق قواعد تضمين النشاط
على تطبيقك. اضبط القيمة على true
للسماح للنظام بتطبيق
عملية تضمين النشاط التي يحدّدها النظام على تطبيقك.
القيود والشروط والتحذيرات
- لا يمكن إلا للتطبيق المضيف للمهمة، والذي يتم تحديده على أنّه مالك النشاط الجذر في المهمة، تنظيم الأنشطة الأخرى وتضمينها في المهمة. إذا كانت الأنشطة التي تتيح الدمج والفواصل تعمل في مهمة تنتمي إلى تطبيق مختلف، لن تعمل ميزة الدمج والفواصل مع هذه الأنشطة.
- لا يمكن تنظيم الأنشطة إلا ضمن مهمة واحدة. يؤدي إطلاق نشاط في مهمة جديدة إلى وضعه دائمًا في نافذة موسّعة جديدة خارج أي عمليات تقسيم حالية.
- لا يمكن تنظيم الأنشطة في العملية نفسها إلا ووضعها في قسم. لا يُبلغ الإجراء المرجعي
SplitInfo
إلا عن الأنشطة التي تنتمي إلى العملية نفسها، لأنّه لا تتوفّر طريقة لمعرفة الأنشطة في العمليات المختلفة. - لا ينطبق كل زوج أو قاعدة نشاط فردية إلا على عمليات بدء النشاط التي تحدث بعد تسجيل القاعدة. لا تتوفّر حاليًا طريقة لتعديل عمليات التقسيم الحالية أو خصائصها المرئية.
- يجب أن تتطابق إعدادات فلتر الأزواج المجزّأة مع النوايا المستخدَمة عند بدء الأنشطة بالكامل. يحدث المطابقة عند بدء نشاط جديد من عملية التطبيق، لذا قد لا يعرف أسماء المكوّنات التي يتم حلّها لاحقًا في عملية النظام عند استخدام المقصودات الضمنية. إذا لم يكن اسم المكوّن معروفًا في وقت الإطلاق، يمكن استخدام بدلاء بدلاً من ذلك ("*/*") ويمكن إجراء الفلترة استنادًا إلى إجراء النية.
- لا تتوفّر حاليًا طريقة لنقل الأنشطة بين الحاويات أو بين عمليات التقسيم والخروج منها بعد إنشائها. لا تنشئ مكتبة WindowManager تقسيمات إلا عند بدء أنشطة جديدة تتضمّن قواعد مطابقة، ويتم إتلاف التقسيمات عند انتهاء النشاط الأخير في حاوية مجزّأة.
- يمكن إعادة تشغيل الأنشطة عند تغيير الإعدادات، لذا عند إنشاء أو إزالة فاصل وتغيير حدود النشاط، يمكن أن يخضع النشاط لعملية تدمير النسخة السابقة بالكامل وإنشاء النسخة الجديدة. ونتيجةً لذلك، على مطوّري التطبيقات الانتباه إلى إجراءات مثل بدء أنشطة جديدة من عمليات الاستدعاء في دورة الحياة.
- يجب أن تتضمّن الأجهزة واجهة إضافات النوافذ لتتمكّن من تضمين النشاط. تتضمّن الواجهة جميع الأجهزة التي تعمل بالإصدار 12L من نظام التشغيل Android (المستوى 32 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث تقريبًا، والتي تعمل بشاشة كبيرة. ومع ذلك، لا تتضمّن بعض الأجهزة ذات الشاشات الكبيرة التي لا يمكنها تشغيل أنشطة متعدّدة واجهة تطبيقات إضافية للنوافذ. إذا كان جهاز الشاشة الكبيرة لا يتيح استخدام وضع المتعدّد النوافذ، قد لا يتيح تضمين الأنشطة.
مصادر إضافية
- Codelabs:
- مسار التعلّم: تضمين الأنشطة
- نموذج التطبيق: تضمين النشاط