يؤدي تضمين الأنشطة إلى تحسين التطبيقات على الأجهزة ذات الشاشات الكبيرة عن طريق تقسيم نافذة مهمة التطبيق بين نشاطين أو مثيلين لنفس النشاط.
إذا كان تطبيقك يتضمّن عدة أنشطة، سيتيح لك تضمين الأنشطة تحسين تجربة المستخدم على الأجهزة اللوحية والأجهزة القابلة للطي وأجهزة ChromeOS.
لا يتطلب تضمين الأنشطة إعادة بناء التعليمات البرمجية. ويمكنك تحديد كيفية عرض تطبيقك أنشطته، جنبًا إلى جنب أو بشكلٍ مجمّع، عن طريق إنشاء ملف إعداد XML أو من خلال إجراء طلبات بيانات من واجهة برمجة التطبيقات Jetpack WindowManager.
ويتم الاحتفاظ بالتوافق مع الشاشات الصغيرة تلقائيًا. عندما يكون تطبيقك على جهاز بشاشة صغيرة، يتم تجميع الأنشطة واحدة فوق الآخر. على الشاشات الكبيرة، يتم عرض الأنشطة جنبًا إلى جنب. يحدد النظام العرض التقديمي بناءً على التكوين الذي قمت بإنشائه - ولا يلزم منطق تشعّب.
يتوافق "تضمين الأنشطة" مع التغييرات في اتجاه الجهاز، ويعمل بسلاسة على الأجهزة القابلة للطي وأنشطة التكديس وفك التكديس عند طيّ الجهاز وفتحه.
تتوفّر ميزة تضمين الأنشطة على معظم الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار 12L من نظام التشغيل Android (المستوى 32 من واجهة برمجة التطبيقات) والإصدارات الأحدث.
تقسيم نافذة المهمة
يعمل تضمين النشاط على تقسيم نافذة مهمة التطبيق إلى حاويتين: أساسية وثانوية. تحتوي الحاويات على أنشطة تم إطلاقها من النشاط الرئيسي أو من أنشطة أخرى موجودة بالفعل في الحاويات.
يتم تكديس الأنشطة في الحاوية الثانوية عند إطلاقها، ويتم تكديس الحاوية الثانوية أعلى الحاوية الأساسية على الشاشات الصغيرة، وبالتالي فإن تكديس الأنشطة والتنقل الخلفي يتماشى مع ترتيب الأنشطة المضمنة بالفعل في تطبيقك.
يتيح لك تضمين الأنشطة عرض الأنشطة بعدة طرق. يمكن لتطبيقك تقسيم نافذة المهمة عن طريق تشغيل نشاطين جنبًا إلى جنب بشكل متزامن:
أو يمكن للنشاط الذي يشغل نافذة المهمة بأكملها إنشاء قسمة من خلال بدء نشاط جديد إلى جانب:
يمكن للأنشطة الموجودة بالفعل مقسّمة وتشارك نافذة مهمة بدء أنشطة أخرى بالطرق التالية:
بجانب أي نشاط آخر:
إلى الجانب، مع تحريك الانقسام إلى الجانبين، مع إخفاء النشاط الأساسي السابق:
ابدأ نشاطًا في مكانه في الأعلى؛ أي في نفس حزمة الأنشطة:
بدء نشاط في نافذة كاملة في نفس المهمة:
التنقّل الخلفي
يمكن أن يكون للأنواع المختلفة من التطبيقات قواعد مختلفة للتنقّل للخلف في حالة نافذة مهمة مقسّمة اعتمادًا على التبعيات بين الأنشطة أو كيفية بدء المستخدمين للحدث الخلفي، على سبيل المثال:
- التعاون معًا: إذا كانت الأنشطة مرتبطة ببعضها البعض، ولا ينبغي عرض أحدهما دون الآخر، يمكن تهيئة التنقل الخلفي لإنهاء كليهما.
- التنفيذ وحده: إذا كانت الأنشطة مستقلة تمامًا، فلن يؤثر التنقل للخلف في النشاط على حالة نشاط آخر في نافذة المهمة.
يتم إرسال الحدث الخلفي إلى آخر نشاط تم التركيز عليه عند استخدام التنقّل باستخدام الأزرار. باستخدام التنقل بالإيماءات، يتم إرسال حدث الرجوع إلى النشاط الذي حدثت فيه الإيماءة.
تصميم متعدد الأجزاء
يتيح لك Jetpack WindowManager إنشاء نشاط يتضمّن تنسيقًا متعدد الأجزاء على الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار Android 12L (المستوى 32 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث وعلى بعض الأجهزة التي تحتوي على إصدارات سابقة من النظام الأساسي. يمكن للتطبيقات الحالية التي تستند إلى أنشطة متعددة بدلاً من الأجزاء أو التنسيقات المستندة إلى طريقة العرض، مثل SlidingPaneLayout
، توفير تجربة مستخدم محسَّنة على الشاشات الكبيرة بدون إعادة ضبط رمز المصدر.
أحد الأمثلة الشائعة هو تقسيم القائمة التفصيلية. لضمان تقديم عرض عالي الجودة، يبدأ النظام نشاط القائمة، ثم يبدأ التطبيق على الفور نشاط التفاصيل. ينتظر نظام الانتقال حتى يتم رسم النشاطين، ثم يعرضهما معًا. بالنسبة للمستخدم، يتم تشغيل النشاطين كنشاط واحد.
سمات التقسيم
يمكنك تحديد كيفية تناسب نافذة المهمة بين حاويات التقسيم وكيفية تخطيط الحاويات بالنسبة إلى بعضها البعض.
بالنسبة إلى القواعد المحدّدة في ملف إعداد XML، اضبط السمات التالية:
splitRatio
: لضبط نسب الحاوية. القيمة هي رقم نقطة عائمة في الفاصل المفتوح (0.0، 1.0).splitLayoutDirection
: تحدّد كيفية تخطيط الحاويات المقسَّمة بالنسبة إلى بعضها البعض. وتتضمّن القيم ما يلي:ltr
: من اليسار إلى اليمينrtl
: من اليمين إلى اليسارlocale
: يتم تحديد إماltr
أوrtl
من إعداد اللغة.
راجِع ضبط XML أدناه للاطّلاع على أمثلة.
بالنسبة إلى القواعد التي تم إنشاؤها باستخدام واجهات برمجة تطبيقات WindowManager API، يمكنك إنشاء كائن 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 للتطبيقات إمكانية حصر الأنشطة بالاتجاه العمودي أو الأفقي. لتحسين تجربة المستخدم على الأجهزة ذات الشاشات الكبيرة، مثل الأجهزة اللوحية والأجهزة القابلة للطي، يمكن للشركات المصنّعة للأجهزة تجاهل طلبات اتجاه الشاشة ووضع التطبيق على شاشة عريضة أفقيًا في الوضع العمودي على الشاشات الأفقية أو الاتجاه الأفقي على الشاشات العمودية.
وبالمثل، عند تفعيل ميزة تضمين الأنشطة، يمكن للمصنّعين الأصليين للأجهزة تخصيص الأجهزة لتتناسب مع الأنشطة في الوضع العمودي الثابت على شاشة عريضة أفقيًا في الاتجاه الأفقي على الشاشات الكبيرة (العرض ≥ 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>
في إصدار WindowManager 1.1.0-alpha06 والإصدارات الأحدث، يتم إيقاف تقسيمات تضمين الأنشطة ما لم تتم إضافة الخاصية إلى البيان وتعيينها على "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
: لضبط الحد الأدنى لعرض الشاشة (بوحدات بكسل مستقلة الكثافة، بكسل مستقل الكثافة) يؤدي إلى تفعيل التقسيم.setMinSmallestWidthDp
: لضبط الحدّ الأدنى للقيمة (بوحدة بكسل مستقلة الكثافة) التي يجب أن تستوفيها القيم الأصغر من سمتَي العرض من أجل تفعيل التقسيم بغض النظر عن اتجاه الجهاز.setMaxAspectRatioInPortrait
: لضبط الحد الأقصى لنسبة العرض إلى الارتفاع (height:width) في الاتجاه العمودي الذي يتم به عرض تقسيمات الأنشطة. إذا تجاوزت نسبة العرض إلى الارتفاع لشاشة عمودية الحد الأقصى لنسبة العرض إلى الارتفاع، يتم إيقاف التقسيمات بغض النظر عن عرض الشاشة. ملاحظة: القيمة التلقائية هي 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
: لضبط الحد الأدنى لعرض العرض (بوحدات بكسل مستقلة الكثافة، وحدة بكسل مستقلة الكثافة) الذي يسمح بالتقسيم.setMinSmallestWidthDp
: لضبط الحدّ الأدنى للقيمة (بوحدة بكسل مستقلة الكثافة) التي يجب أن تحتويها الأصغر من سمتَي العرض للسماح بالتقسيم بغض النظر عن اتجاه الجهاز.setMaxAspectRatioInPortrait
: لضبط الحد الأقصى لنسبة العرض إلى الارتفاع (height:width) في الاتجاه العمودي الذي يتم به عرض تقسيمات الأنشطة. ملاحظة: القيمة التلقائية هي 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 المتعدّدة. يعرض النظام نشاطًا للتطبيق المضيف ونشاطًا مضمّنًا من تطبيق آخر على الشاشة جنبًا إلى جنب أو أعلى وأسفل، كما هو الحال في تضمين نشاط التطبيق الواحد.
على سبيل المثال، يمكن لتطبيق "الإعدادات" تضمين نشاط أداة اختيار الخلفية من تطبيق Background 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 في أعلى القائمة. عندما يتنقل المستخدم مرة أخرى من B، تظهر 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 والنشاط "ج" الذي يتم تشغيله عادةً من 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>
إنهاء الأنشطة
يمكن للمستخدمين إنهاء الأنشطة على أي من جانبي التقسيم من خلال التمرير السريع من حافة الشاشة:
إذا تم إعداد الجهاز لاستخدام زر الرجوع بدلاً من التنقل بالإيماءات، فسيتم إرسال الإدخال إلى النشاط محل التركيز، وهو النشاط الذي تم لمسه أو تشغيله آخر مرة.
يعتمد التأثير الذي يحدثه إنهاء جميع الأنشطة في الحاوية على الحاوية المقابلة على إعدادات التقسيم.
سمات الإعداد
يمكنك تحديد سمات قاعدة الزوج المنقسم لضبط مدى تأثير إنهاء جميع الأنشطة على جانب واحد من عملية التقسيم في الأنشطة على الجانب الآخر من التقسيم. السمات هي:
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>
إنهاء أنشطة متعددة في حاويات
في حال تكديس عدة أنشطة في حاوية مقسَّمة، لا يؤدي إنهاء نشاط في أسفل الحزمة إلى إنهاء الأنشطة تلقائيًا في الأعلى.
على سبيل المثال، إذا كان هناك نشاطان في الحاوية الثانوية، تكون C أعلى من B:
ويتم تحديد ضبط التقسيم من خلال تهيئة النشاطين "أ" و"ب":
<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 (المصنّعون الأصليون للأجهزة أو المصنّعين الأصليين للأجهزة) تنفيذ تضمين الأنشطة كوظيفة لنظام الجهاز. ويحدّد النظام قواعد التقسيم للتطبيقات المتعددة الأنشطة، ما يُلغي سلوك نافذة التطبيقات. يفرض إلغاء النظام للتطبيقات المتعددة الأنشطة الدخول في وضع تضمين النشاط الذي يحدده النظام.
إنّ تضمين أنشطة النظام يمكن أن يحسّن من عرض التطبيق من خلال تنسيقات متعددة الأجزاء، مثل 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>
يتم تحديد اسم السمة في الكائن WindowProperties في Jetpack WindowManager.
اضبط القيمة على false
إذا كان تطبيقك ينفّذ تضمين الأنشطة، أو إذا أردت منع النظام من تطبيق قواعد تضمين الأنشطة على تطبيقك. اضبط القيمة على true
للسماح للنظام بتطبيق تضمين النشاط الذي يحدده النظام على تطبيقك.
القيود والقيود والتنبيهات
- لا يمكن تنظيم الأنشطة الأخرى وتضمينها في المهمة سوى التطبيق المضيف للمهمة، والذي يتم تحديده على أنه مالك النشاط الجذري في المهمة. إذا كانت الأنشطة التي تتيح التضمين والتقسيم تعمل في مهمة ينتمي إلى تطبيق مختلف، فلن يصلح التضمين والتقسيم لتلك الأنشطة.
- لا يمكن تنظيم الأنشطة إلا في مهمة واحدة. يؤدي بدء نشاط في مهمة جديدة دائمًا إلى وضعه في نافذة موسّعة جديدة خارج أي تقسيمات حالية.
- يمكن تنظيم الأنشطة في نفس العملية فقط وتقسيمها. لا تُبلغ
عملية معاودة الاتصال
SplitInfo
إلا عن الأنشطة التي تنتمي إلى العملية نفسها، وذلك لعدم توفُّر طريقة لمعرفة الأنشطة في عمليات مختلفة. - تنطبق كل قاعدة نشاط فردي أو زوج واحد فقط على عمليات إطلاق النشاط التي تحدث بعد تسجيل القاعدة. لا توجد حاليًا طريقة لتحديث التقسيمات الحالية أو خصائصها المرئية.
- يجب أن تتطابق إعدادات فلتر الزوج المنقسم مع الأهداف المستخدمة عند تشغيل الأنشطة بالكامل. تحدث المطابقة في المرحلة التي يتم فيها بدء نشاط جديد من عملية التقديم، لذلك قد لا يعرف أسماء المكونات التي يتم حلها لاحقًا في عملية النظام عند استخدام الأهداف الضمنية. إذا لم يكن اسم المكوِّن معروفًا في وقت الإطلاق، يمكن استخدام حرف بدل ("*/*") بدلاً من ذلك، ويمكن تنفيذ الفلترة استنادًا إلى إجراء الهدف.
- لا توجد حاليًا طريقة لنقل الأنشطة بين الحاويات أو داخل وخارج التقسيمات بعد إنشائها. لا يتم إنشاء التقسيمات إلا بواسطة مكتبة WindowManager عند إطلاق أنشطة جديدة ذات قواعد مطابقة، ويتم إتلاف التقسيمات عند انتهاء النشاط الأخير في حاوية مقسَّمة.
- يمكن إعادة تشغيل الأنشطة بعد تغيير الإعدادات، وبالتالي عند إنشاء قسم أو إزالته وتغيير حدود النشاط، قد يمرّ النشاط بالتلف الكامل للمثيل السابق وإنشاء المثيل الجديد. ونتيجةً لذلك، يجب أن يكون مطورو التطبيقات حذرين بشأن أشياء مثل إطلاق أنشطة جديدة من استدعاءات مراحل النشاط.
- يجب أن تتضمّن الأجهزة واجهة إضافات النوافذ لإتاحة تضمين الأنشطة. تشتمل معظم الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار 12L (المستوى 32 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث على الواجهة. ومع ذلك، لا تتضمّن بعض الأجهزة ذات الشاشات الكبيرة والتي لا يمكنها تشغيل أنشطة متعدّدة واجهة إضافات النوافذ. وفي حال عدم توافق الجهاز ذي الشاشة الكبيرة مع وضع النوافذ المتعددة، قد لا يتيح تضمين الأنشطة.
مصادر إضافية
- درس تطبيقي حول الترميز — إنشاء تنسيق تفاصيل القائمة باستخدام تضمين الأنشطة والتصميم المتعدد الأبعاد
- المسار التعليمي - تضمين الأنشطة