يشير التنقّل إلى الطريقة التي يتنقّل بها المستخدمون في تطبيقك. يتفاعل المستخدمون مع عناصر واجهة المستخدم، عادةً من خلال النقر عليها، ويستجيب التطبيق من خلال عرض محتوى جديد. إذا أراد المستخدم الرجوع إلى المحتوى السابق، يمكنه استخدام إيماءة الرجوع أو النقر على زر الرجوع.
نمذجة حالة التنقّل
إحدى الطرق الملائمة لنمذجة هذا السلوك هي استخدام مجموعة من المحتوى. عندما يتنقّل المستخدم إلى الأمام لعرض محتوى جديد، يتم وضعه في أعلى المجموعة. وعندما يرجع من هذا المحتوى، تتم إزالته من المجموعة ويتم عرض المحتوى السابق. في ما يتعلّق بالتنقّل، يُشار عادةً إلى هذه المجموعة باسم مجموعة الرجوع لأنّها تمثّل المحتوى الذي يمكن للمستخدم الرجوع إليه.
إنشاء مجموعة رجوع
في Navigation 3، لا تحتوي مجموعة الأنشطة السابقة فعليًا على محتوى. بدلاً من ذلك، تحتوي على مراجع للمحتوى، تُعرف باسم المفاتيح. يمكن أن تكون المفاتيح أي نوع، ولكنها عادةً ما تكون فئات بيانات بسيطة وقابلة للتسلسل. إنّ استخدام المراجع بدلاً من المحتوى له المزايا التالية:
- من السهل التنقّل من خلال وضع المفاتيح في الأنشطة السابقة.
- طالما أنّ المفاتيح قابلة للتسلسل، يمكن حفظ الأنشطة السابقة في مساحة تخزين مستمرة، ما يسمح لها بالبقاء بعد تغييرات الإعدادات وإيقاف العملية نهائيًا. هذا مهم لأنّ المستخدمين يتوقّعون مغادرة تطبيقك والرجوع إليه لاحقًا واستئناف ما كانوا يفعلونه مع عرض المحتوى نفسه. يمكنك الاطّلاع على مقالة حفظ الأنشطة السابقة لمزيد من المعلومات.
أحد المفاهيم الأساسية في Navigation 3 API هو أنّك تملك مجموعة الرجوع. المكتبة:
- تتوقّع أن تكون مجموعة الرجوع عبارة عن
List<T>مستندة إلى حالة اللقطة، حيثTهو نوعkeysفي مجموعة الرجوع. يمكنك استخدامAnyأو يمكنك تقديم مفاتيح خاصة بك أكثر تحديدًا. عندما ترى المصطلحَين "وضع" أو "إزالة"، يكون التنفيذ الأساسي هو إضافة عناصر أو إزالتها من نهاية القائمة. - تراقب الأنشطة السابقة وتعكس حالتها في واجهة المستخدم باستخدام
NavDisplay.
يوضّح المثال التالي كيفية إنشاء المفاتيح ومجموعة الرجوع وتعديل مجموعة الرجوع استجابةً لأحداث تنقّل المستخدم:
// Define keys that will identify content data object ProductList data class ProductDetail(val id: String) @Composable fun MyApp() { // Create a back stack, specifying the key the app should start with val backStack = remember { mutableStateListOf<Any>(ProductList) } // Supply your back stack to a NavDisplay so it can reflect changes in the UI // ...more on this below... // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state backStack.add(ProductDetail(id = "ABC")) // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state backStack.removeLastOrNull() }
ربط المفاتيح بالمحتوى
يتم نمذجة المحتوى في التنقّل 3 باستخدام NavEntry، وهو فئة تحتوي على دالة مركّبة. تمثّل هذه الفئة وجهة، أي جزءًا واحدًا
من المحتوى يمكن للمستخدم التنقّل إليه و الرجوع منه.
يمكن أن يحتوي NavEntry أيضًا على بيانات وصفية، أي معلومات عن المحتوى. يمكن أن تقرأ كائنات الحاوية، مثل NavDisplay، هذه البيانات الوصفية لمساعدتها في تحديد كيفية عرض محتوى NavEntry. على سبيل المثال، يمكن استخدام البيانات الوصفية لتجاوز الرسوم المتحركة التلقائية لـ NavEntry معيّن. metadata في NavEntry هي خريطة من مفاتيح String إلى قيم Any، ما يوفّر تخزين بيانات متعدد الاستخدامات.
لتحويل key إلى NavEntry، يمكنك إنشاء موفّر إدخالات. هذه دالة تقبل key وتعرض NavEntry لهذا key. يتم تعريفها عادةً كمعلَمة lambda عند إنشاء NavDisplay.
هناك طريقتان لإنشاء موفّر إدخالات، إما من خلال إنشاء دالة lambda
مباشرةً أو باستخدام entryProvider لغة النطاق الخاصة.
إنشاء دالة موفّر إدخالات مباشرةً
عادةً ما تنشئ دالة موفّر إدخالات باستخدام عبارة when، مع فرع لكل مفتاح من مفاتيحك.
entryProvider = { key -> when (key) { is ProductList -> NavEntry(key) { Text("Product List") } is ProductDetail -> NavEntry( key, metadata = mapOf("extraDataKey" to "extraDataValue") ) { Text("Product ${key.id} ") } else -> { NavEntry(Unit) { Text(text = "Invalid Key: $it") } } } }
استخدام لغة النطاق الخاصة entryProvider
يمكن أن تبسّط لغة النطاق الخاصة entryProvider دالة lambda من خلال تجنُّب الحاجة إلى إجراء اختبار لكل نوع من أنواع المفاتيح وإنشاء NavEntry لكل نوع.
استخدِم دالة أداة الإنشاء entryProvider لهذا الغرض. تتضمّن هذه اللغة أيضًا سلوكًا احتياطيًا تلقائيًا (عرض خطأ) في حال عدم العثور على المفتاح.
entryProvider = entryProvider { entry<ProductList> { Text("Product List") } entry<ProductDetail>( metadata = mapOf("extraDataKey" to "extraDataValue") ) { key -> Text("Product ${key.id} ") } }
يُرجى ملاحظة ما يلي من المقتطف البرمجي:
- يتم استخدام
entryلتعريفNavEntryبالنوع المحدّد والمحتوى القابل للإنشاء entryتقبل مَعلمةmetadataلضبطNavEntry.metadata
عرض الأنشطة السابقة
تمثّل مجموعة الرجوع حالة التنقّل في تطبيقك. عندما تتغيّر مجموعة الأنشطة السابقة، يجب أن تعكس واجهة مستخدم التطبيق حالة مجموعة الأنشطة السابقة الجديدة. في Navigation 3، يراقب NavDisplay مجموعة الأنشطة السابقة ويعدّل واجهة المستخدم وفقًا لذلك. يمكنك إنشاء `NavDisplay` باستخدام المَعلمات التالية:
- الأنشطة السابقة - يجب أن يكون نوعها
SnapshotStateList<T>، حيث يكونTهو نوع مفاتيح الأنشطة السابقة. هذهListقابلة للمراقبة، لذا تؤدي إلى إعادة التكوينNavDisplayعند تغيُّرها. entryProviderلتحويل المفاتيح في الأنشطة السابقة إلى عناصرNavEntry.- يمكنك اختياريًا تقديم دالة lambda إلى المَعلمة
onBack. يتم استدعاء هذه الدالة عندما ينفّذ المستخدم حدث رجوع.
يوضّح المثال التالي كيفية إنشاء NavDisplay.
data object Home data class Product(val id: String) @Composable fun NavExample() { val backStack = remember { mutableStateListOf<Any>(Home) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, entryProvider = { key -> when (key) { is Home -> NavEntry(key) { ContentGreen("Welcome to Nav3") { Button(onClick = { backStack.add(Product("123")) }) { Text("Click to navigate") } } } is Product -> NavEntry(key) { ContentBlue("Product ${key.id} ") } else -> NavEntry(Unit) { Text("Unknown route") } } } ) }
بشكلٍ تلقائي، يعرض NavDisplay أعلى NavEntry في الأنشطة السابقة في تنسيق لوحة واحدة. يعرض التسجيل التالي هذا التطبيق قيد التشغيل:
NavDisplay مع وجهتَيندورة حياة الوجهة
يستخدم NavDisplay عناصر مخصّصة LifecycleOwners للحدّ من حالة دورة حياة
NavEntry استنادًا إلى القيود على مستوى المشهد والقيود على مستوى الإدخال.
لمزيد من المعلومات عن دورات الحياة في Compose، يمكنك الاطّلاع على مقالة دورة الحياة في Jetpack Compose.
قيود دورة الحياة على مستوى المشهد
NavDisplay يدير دورة حياة Scene النشطة. يتم تحديد الحدود القصوى على مستوى المشهد على النحو التالي:
بالنسبة إلى المشاهد غير المتراكبة:
RESUMED: يُسمح بها فقط عندما يكون انتقال المشهد قد استقر ولم تكن هناك مشاهد متراكبة نشطة معروضة فوقه.STARTED: يتم الحدّ من الحالة إلىSTARTEDأثناء انتقالات المشهد، مثل التنقّل إلى الأمام أو الخلف أو عندما يكون المشهد مغطّى بمشهد متراكب.
بالنسبة إلى المشاهد المتراكبة، مثل مربّعات الحوار أو البطاقات السفلية:
RESUMED: يُسمح بها فقط للمشهد المتراكب النشط الحالي في الأعلى.STARTED: يتم الحدّ من الحالة إلىSTARTEDلأي مشاهد متراكبة أساسية مغطّاة بمشهد متراكب أحدث.
حالة دورة الحياة على مستوى الإدخال
تدير المكتبة حالة دورة الحياة القصوى لكل NavEntry فردي استنادًا إلى وجوده في الأنشطة السابقة:
RESUMED: إذا كان الإدخال موجودًا في مجموعة الرجوع الحالية، يُسمح لدورة حياته بالوصول إلىRESUMED(مع مراعاة الحدّ الأقصى على مستوى المشهد).CREATED: إذا لم يعُد الإدخال في مجموعة الرجوع، مثلما يحدث عند إزالته من المجموعة ولكن لا يزال يتم عرضه على الشاشة أثناء تحريكه للخارج، فإنّ المكتبة تحدّ من دورة حياته بشكلٍ صارم عندCREATED. يضمن هذا الحدّ توقّف الإدخالات في الخلفية أو الخارجة عن تنفيذ العمل النشط، مثل جمع التدفقات أو تشغيل إجراءات فرعية مرتبطة بالحالتَينRESUMEDأوSTARTEDأثناء إكمال انتقالات الخروج.
كيفية الجمع بينهما
على سبيل المثال، يتم تحديد حالة دورة الحياة النهائية لـ NavEntry على النحو التالي:
| السيناريو | الحدّ الأقصى على مستوى المشهد | الحدّ الأقصى على مستوى الإدخال | الحدّ الأقصى الفعلي |
|---|---|---|---|
| إدخال نشط، شاشة مستقرة (بدون انتقالات أو مشاهد متراكبة) | RESUMED |
RESUMED |
RESUMED |
| إدخال نشط، أثناء الانتقال (التنقّل إلى أو من) | STARTED |
RESUMED |
STARTED |
| إدخال نشط، مغطّى بمشهد متراكب (على سبيل المثال، تم فتح مربّع حوار) | STARTED |
RESUMED |
STARTED |
| إدخال تمت إزالته من المجموعة، ويتم تحريكه للخارج | STARTED أو RESUMED |
CREATED |
CREATED |
خلاصة ما سبق ذكره
يوضّح المخطّط التالي كيفية تدفق البيانات بين الكائنات المختلفة في Navigation 3:
تبدأ أحداث التنقّل التغييرات. تتم إضافة المفاتيح أو إزالتها من مجموعة الرجوع استجابةً لتفاعلات المستخدم.
يؤدي تغيير حالة الأنشطة السابقة إلى استرداد المحتوى. يراقب
NavDisplay(دالة مركّبة تعرض الأنشطة السابقة) الأنشطة السابقة. في الإعداد التلقائي، يعرض أعلى إدخال في مجموعة الأنشطة السابقة في تنسيق لوحة واحدة. عندما يتغيّر المفتاح العلوي في مجموعة الأنشطة السابقة، يستخدمNavDisplayهذا المفتاح لطلب المحتوى المقابل من موفّر الإدخالات.يوفّر موفّر الإدخالات المحتوى. موفّر الإدخالات هو دالة تربط مفتاحًا بـ
NavEntry. عند تلقّي مفتاح منNavDisplay، يقدّم موفّر الإدخالاتNavEntryالمرتبط، الذي يحتوي على كل من المفتاح والمحتوى.يتم عرض المحتوى. يتلقّى
NavDisplayعنصرNavEntryويعرض المحتوى.