يؤدي تضمين الأنشطة إلى تحسين التطبيقات على الأجهزة ذات الشاشات الكبيرة عن طريق تقسيم نافذة مهمة التطبيق بين نشاطين أو حالتين من نفس النشاط الأخرى.
إذا كان تطبيقك يتألف من أنشطة متعددة، تتيح لك ميزة "تضمين الأنشطة" تقديم تجربة محسّنة للمستخدمين على الأجهزة اللوحية والأجهزة القابلة للطي وأجهزة ChromeOS.
لا يتطلب تضمين الأنشطة إعادة بناء التعليمات البرمجية. أنت من يحدِّد كيفية استخدام تطبيقك أن تعرض أنشطتها — جنبًا إلى جنب أو بشكلٍ مكدس — عن طريق إنشاء ملف XML ملف الإعداد أو من خلال إجراء طلبات بيانات من واجهة برمجة التطبيقات Jetpack WindowManager.
يتم تلقائيًا الاحتفاظ بتوافق التطبيق مع الشاشات الصغيرة. عندما يكون تطبيقك على جهاز ذي شاشة صغيرة، فإن الأنشطة مكدسة واحدة فوق الأخرى. على الشاشات الكبيرة، يتم عرض الأنشطة جنبًا إلى جنب. يحدد النظام عرض تقديمي استنادًا إلى الإعداد الذي أنشأته، بدون منطق تشعّب مطلوبة.
تتيح ميزة "تضمين الأنشطة" تغيير اتجاه الجهاز، وتعمل بسلاسة على الأجهزة القابلة للطي، كما تتيح تجميع الأنشطة وتفكيكها أثناء طي الجهاز وفتحه.
تتوفّر ميزة تضمين الأنشطة على معظم الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار 12L من نظام التشغيل Android (المستوى 32 لواجهة برمجة التطبيقات) والإصدارات الأحدث.
تقسيم نافذة المهمة
تؤدي عملية تضمين النشاط إلى تقسيم نافذة مهمة التطبيق إلى حاويتَين: حاوية أساسية و حاوية ثانوية. تحتوي الحاويات على الأنشطة التي تم إطلاقها من النشاط الرئيسي أو من أنشطة أخرى موجودة في الحاويات.
يتم تكديس الأنشطة في الحاوية الثانوية عند إطلاقها، يتم تكديس الحاوية الثانوية فوق الحاوية الأساسية على الشاشات الصغيرة، بحيث يتوافق تكديس النشاط والتنقل الخلفي مع ترتيب الأنشطة المضمنة بالفعل في تطبيقك.
تتيح لك ميزة "تضمين الأنشطة" عرض الأنشطة بعدة طرق. يمكن لتطبيقك تقسيم نافذة المهام من خلال تشغيل نشاطَين جنبًا إلى جنب في الوقت نفسه:
أو يمكن للنشاط الذي يشغل نافذة المهمة بأكملها إنشاء قسمة حسب وإطلاق نشاط جديد إلى جانب:
يمكن للأنشطة التي تم تقسيمها ومشاركة نافذة مهمة فيها تشغيل أنشطة أخرى بالطرق التالية:
بجانب أي نشاط آخر:
إلى الجانب، وحرِّك القسم إلى الجانب، ما يؤدي إلى إخفاء النشاط الأساسي السابق:
ابدأ نشاطًا في مكانه في أعلى الصفحة، أي في مجموعة الأنشطة نفسها:
افتح نافذة نشاط كاملة في المهمة نفسها:
التنقّل الخلفي
يمكن أن يكون للأنواع المختلفة من التطبيقات قواعد مختلفة للانتقال للخلف في حالة نافذة تقسيم المهمة اعتمادًا على التبعيات بين الأنشطة أو كيف المستخدمون الذين يبدأون الحدث الخلفي، مثل:
- التعاون معًا: إذا كانت الأنشطة مرتبطة ببعضها البعض، ويجب عدم عرض أحدها بدون الآخر، يمكن تهيئة التنقل الخلفي لإنهاء كليهما.
- افعل ذلك بمفردك: إذا كانت الأنشطة مستقلة تمامًا، فيمكنك الرجوع إلى الصفحة السابقة لا يؤثر النشاط في حالة نشاط آخر في نافذة المهمة.
يتم إرسال حدث الرجوع إلى آخر نشاط تم التركيز عليه عند استخدام زر التنقّل.
للتنقّل بالاستناد إلى الإيماءات:
الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) والإصدارات الأقدم: يتم إرسال حدث الرجوع إلى النشاط الذي حدثت فيه الإيماءة. عندما يمرر المستخدمون سريعًا من الجانب الأيسر للشاشة، يتم إرسال حدث الرجوع إلى النشاط في اللوحة اليسرى من النافذة المُقسّمة. عندما يمرر المستخدمون سريعًا من الجانب الأيمن من الشاشة، يتم إرسال حدث الرجوع إلى النشاط في اللوحة اليمنى.
الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات) والإصدارات الأحدث
عند التعامل مع أنشطة متعددة من التطبيق نفسه، تؤدي الإيماءة إلى إنهاء النشاط العلوي بغض النظر عن اتجاه التمرير السريع، ما يقدّم تجربة أكثر اتساقًا.
في السيناريوهات التي تتضمّن نشاطَين من تطبيقَين مختلفَين (تداخل)، يتم توجيه حدث الرجوع إلى النشاط الأخير الذي كان في المقدّمة، بما يتوافق مع سلوك التنقّل باستخدام الأزرار.
تنسيق متعدد الأقسام
يتيح لك Jetpack WindowManager إنشاء نشاط يتضمن أجزاءً متعددة.
التنسيق على الأجهزة ذات الشاشات الكبيرة التي تعمل بالإصدار 12L (المستوى 32 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث من نظام التشغيل Android
بعض الأجهزة التي تعمل بإصدارات سابقة للنظام الأساسي التطبيقات الحالية التي تستند إلى
أنشطة متعددة بدلاً من الأجزاء أو التخطيطات القائمة على العرض مثل
بإمكان 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) تجاهل طلبات اتجاه الشاشة وعرض التطبيق في وضع شاشة عريضة عمودية على الشاشات الأفقية أو وضع شاشة عريضة أفقية على الشاشات الرأسية.
وبالمثل، عند تفعيل ميزة تضمين الأنشطة، يمكن لمصنّعي المعدّات الأصلية تخصيص الأجهزة لعرض الأنشطة في وضع مُعدّ للعرض على شاشة عريضة في الوضع العمودي على الشاشات الكبيرة (يجب أن يكون العرض أكبر من أو يساوي 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> في ملف بيان التطبيق، وتعيين إلى "صواب"، على سبيل المثال:<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <property android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED" android:value="true" /> </application> </manifest>
تقسيمات تضمين الأنشطة في إصدار WindowManager 1.1.0-alpha06 والإصدارات الأحدث ما لم تتم إضافة السمة إلى البيان وضبطها على "صحيح".
ويستخدم مصنعو الأجهزة هذا الإعداد أيضًا لتفعيل إمكانات مخصّصة ل التطبيقات التي تتيح تضمين الأنشطة. على سبيل المثال، يمكن للأجهزة عرض نشاط في وضع مربّع على شاشة أفقية لتوجيه النشاط إلى وضع عمودي عند الانتقال إلى تنسيق نوافذ مزدوجة عند بدء نشاط ثانٍ (راجِع الوضع الثابت بالاتجاه العمودي).
إعدادات XML
لإنشاء تنفيذ مستند إلى XML لتضمين الأنشطة، أكمل الخطوات التالية:
أنشئ ملف موارد 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 ويُتيح القواعد للنظام. الحقيبة النفاثة تجعل مكتبة بدء التشغيلInitializer
ملف 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()
: لضبط الحدّ الأقصى لأبعاد العرض النسبة (height:width) في الاتجاه العمودي الذي يعرض فيه النشاط يتم عرض التقسيمات. إذا كانت نسبة العرض إلى الارتفاع لشاشة في الوضع العمودي تتعدى الحد الأقصى لنسبة العرض إلى الارتفاع، يتم إيقاف عمليات التقسيم بغض النظر عن عرض الشاشة. ملاحظة: القيمة التلقائية هي 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()
: لضبط الحد الأدنى لعرض العرض (بوحدات بكسل مستقلة الكثافة، وحدة بكسل مستقلة الكثافة) تسمح بالتقسيم.-
setMinSmallestWidthDp()
: تُستخدَم لضبط الحد الأدنى للقيمة (بالوحدة dp) التي يجب أن يمتلكها أصغر سمة من سمتَي الشاشة للسماح بالتقسيم بغض النظر عن اتجاه الجهاز. 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 المتعددة. يعرض النظام نشاطًا للتطبيق المضيف ونشاطًا مضمّنًا من تطبيق آخر على الشاشة جنبًا إلى جنب أو في أعلى الشاشة أو أسفلها تمامًا كما هو الحال في تضمين نشاط تطبيق واحد.
على سبيل المثال، يمكن أن يدمج تطبيق "الإعدادات" نشاط أداة اختيار الخلفية من تطبيق 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 أو في وقت التشغيل باستخدام 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 في أعلى القائمة. عندما يعود المستخدم من "ب"، تظهر "أ" بدلاً من القائمة.
يجب إزالة الشاشة "أ" من الحزمة الخلفية في مثل هذه الحالات.
السلوك التلقائي عند الإطلاق على الجانب في حاوية جديدة فوق 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 بعد الانتهاء من 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 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. }
في حال لم تكن التقسيمات متاحة، يتم إطلاق الأنشطة فوق النشاط. المكدس (باتباع نموذج التضمين غير النشط).
منع تجاوز إعدادات النظام
الشركات المصنّعة لأجهزة Android (أو الشركات الأصلية للأجهزة المصنّعين الأصليين للأجهزة)، يمكنهم تنفيذ تضمين الأنشطة كوظيفة لنظام الجهاز. يحدِّد النظام قواعد تقسيم التطبيقات المتعدّدة الأنشطة، ما يؤدي إلى إلغاء سلوك استخدام النوافذ للتطبيقات. يفرض إلغاء النظام تثبيت التطبيقات المتعدّدة الأنشطة وضع تضمين النشاط الذي يحدده النظام.
يمكن أن يؤدي تضمين أنشطة النظام إلى تحسين عرض التطبيق من خلال أجزاء متعددة التنسيقات، مثل list-detail، بدون أي تغييرات في التطبيق. ومع ذلك، فإن تضمين أنشطة النظام أيضًا قد يتسبب في وجود أخطاء في تخطيطات التطبيق أو أخطاء أو مع تضمين الأنشطة التي يُنفذها التطبيق.
يمكن لتطبيقك منع تضمين أنشطة النظام أو السماح به من خلال ضبط الموقع في ملف بيان التطبيق، على سبيل المثال:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<property
android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
android:value="true|false" />
</application>
</manifest>
يتم تحديد اسم الموقع في Jetpack WindowManager WindowProperties
الخاص بك. اضبط القيمة على false
إذا كان تطبيقك ينفذ عملية تضمين النشاط، أو
إذا كنت تريد منع النظام من تطبيق قواعد تضمين النشاط
على تطبيقك. اضبط القيمة على true
للسماح للنظام بتطبيق
عملية تضمين النشاط التي يحدّدها النظام على تطبيقك.
القيود والشروط والتحذيرات
- التطبيق المضيف للمهمة فقط، والذي يتم تحديده كمالك للجذر نشاطهم في المهمة، يمكنهم تنظيم الأنشطة الأخرى وتضمينها في المهمة. إذا كانت الأنشطة التي تتيح عمليات الدمج والاقتطاع تعمل في مهمة تنتمي إلى تطبيق مختلف، لن تعمل عمليات الدمج والاقتطاع في هذه الأنشطة.
- لا يمكن تنظيم الأنشطة إلا في مهمة واحدة. إطلاق نشاط في مهمة جديدة دائمًا في نافذة موسعة جديدة خارج أي التقسيمات الحالية.
- يمكن تنظيم الأنشطة في نفس العملية فقط وتقسيمها. تشير رسالة الأشكال البيانية
لا تقدم ميزة معاودة الاتصال
SplitInfo
إلا تقارير عن الأنشطة التي تنتمي إلى النطاق نفسه. نظرًا لعدم وجود طريقة لمعرفة الأنشطة في مختلف والعمليات. - لا ينطبق كل زوج أو قاعدة نشاط فردية إلا على عمليات بدء النشاط التي تحدث بعد تسجيل القاعدة. لا توجد حاليًا طريقة لتعديل التقسيمات الحالية أو خصائصها المرئية.
- يجب أن تتطابق إعدادات فلتر الزوج المقسّم مع الأهداف المستخدمة عند إطلاق الأنشطة بالكامل. تحدث المطابقة عند نقطة بداية أن يتم بدء النشاط من عملية تقديم الطلب، لذا قد لا يعرف أسماء المكونات التي يتم حلها لاحقًا في عملية النظام عند استخدام أغراض ضمنية. إذا لم يكن اسم مكون معروفًا في وقت الإطلاق، يمكن استخدام حرف البدل بدلاً من ذلك ("*/*") ويمكن إجراء الفلترة استنادًا إلى على الإجراء المقصود.
- لا توجد حاليًا طريقة لنقل الأنشطة بين الحاويات أو داخل من التقسيمات بعد إنشائها. لا تنشئ مكتبة WindowManager تقسيمات إلا عند بدء أنشطة جديدة تتضمّن قواعد مطابقة، ويتم إتلاف التقسيمات عند انتهاء النشاط الأخير في حاوية مجزّأة.
- يمكن إعادة تشغيل الأنشطة عند تغيير الإعدادات، لذا عند إنشاء أو إزالة فاصل وتغيير حدود النشاط، يمكن أن يخضع النشاط لعملية تدمير النسخة السابقة بالكامل وإنشاء النسخة الجديدة. ونتيجةً لذلك، على مطوّري التطبيقات الانتباه إلى إجراءات مثل بدء أنشطة جديدة من عمليات الاستدعاء في دورة الحياة.
- يجب أن تتضمّن الأجهزة واجهة إضافات النوافذ لتتمكّن من تضمين النشاط. تتضمّن الواجهة جميع الأجهزة التي تعمل بالإصدار 12L من نظام التشغيل Android (المستوى 32 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث تقريبًا. ومع ذلك، فإن بعض الأجهزة ذات الشاشات الكبيرة التي غير قادرين على تشغيل أنشطة متعددة لا تشمل النافذة مع واجهة المستخدم الإضافية. إذا كان الجهاز الذي يحتوي على شاشة كبيرة لا يتيح عرض النوافذ المتعددة فقد لا يتيح تضمين الأنشطة.
مصادر إضافية
- Codelabs:
- المسار التعليمي - تضمين الأنشطة
- نموذج التطبيق — activity-embedding