يوفر مكوِّن التنقل لغة خاصة بالنطاق تستند إلى Kotlin،
DSL التي تعتمد على نموذج أمان من نوع Kotlin
البنّائون
. تتيح لك واجهة برمجة التطبيقات هذه إنشاء الرسم البياني بشكل صريح في رمز Kotlin، بدلاً من
من داخل مورد XML. وقد يكون هذا مفيدًا إذا كنت تريد إنشاء تصميم،
التنقل بشكل ديناميكي. على سبيل المثال، يمكن لتطبيقك تنزيل ملف
تهيئة التنقل من خدمة ويب خارجية ثم استخدم ذلك
لإنشاء رسم بياني للتنقل بشكل ديناميكي في صفحة
onCreate()
.
التبعيات
لاستخدام Kotlin DSL مع تجزئة، أضف التبعية التالية إلى ملف
ملف build.gradle
:
Groovy
dependencies { def nav_version = "2.8.0" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.8.0" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
إنشاء رسم بياني
فيما يلي مثال أساسي على نموذج دوار الشمس
التطبيق. لهذا الغرض
على سبيل المثال، لدينا وجهتان: home
وplant_detail
. home
وجود الوجهة عند تشغيل المستخدم للتطبيق لأول مرة. هذه الوجهة
تعرض قائمة بالنباتات من حديقة المستخدم. عندما يحدد المستخدم أحد
النباتات، ينتقل التطبيق إلى وجهة plant_detail
.
يوضح الشكل 1 هذه الوجهات مع الوسيطات المطلوبة بواسطة
وجهة واحدة (plant_detail
) وإجراء، to_plant_detail
، يستخدمه التطبيق
الانتقال من home
إلى plant_detail
.
استضافة الرسم البياني Nav من Kotlin DSL
قبل أن تتمكن من إنشاء رسم بياني للتنقل في تطبيقك، تحتاج إلى مكان لاستضافة
الرسم البياني. يستخدم هذا المثال الأجزاء، لذا فهو يستضيف الرسم البياني
NavHostFragment
في داخل
FragmentContainerView
:
<!-- activity_garden.xml -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true" />
</FrameLayout>
لاحِظ أنّه لم يتم ضبط السمة app:navGraph
في هذا المثال. الرسم البياني
على أنّه مورد في
المجلد res/navigation
، لذا يجب ضبطه كجزء من onCreate()
أثناء النشاط.
في XML، يربط الإجراء معرّف الوجهة مع وسيطة واحدة أو أكثر. ومع ذلك، عند استخدام DSL للتنقل، يمكن أن يحتوي المسار على وسيطات كجزء من المسار. وهذا يعني أنه لا يوجد مفهوم للإجراءات عند استخدام DSL.
الخطوة التالية هي تحديد المسارات التي ستستخدمها عند تحديد الرسم البياني.
إنشاء مسارات للرسم البياني
يتم تحليل الرسومات البيانية للتنقل المستندة إلى XML كجزء
في عملية تصميم Android. يتم إنشاء ثابت رقمي لكل id
.
المحددة في الرسم البياني. أرقام التعريف الثابتة التي تم إنشاؤها خلال وقت الإصدار ليست
متاحة عند إنشاء الرسم البياني للتنقل في وقت التشغيل بحيث توفر ميزة التنقل DSL
تستخدم serialable
أنواعًا بدلاً من
المعرفات. يتم تمثيل كل مسار بنوع فريد.
عند التعامل مع الوسيطات، يتم تضمينها في المسار النوع. يتيح لك ذلك تأمين الكتابة لوسيطات التنقل.
@Serializable data object Home
@Serializable data class Plant(val id: String)
إنشاء رسم بياني باستخدام NavGraphBuilder DSL
بعد تحديد المسارات، يمكنك إنشاء رسم بياني للتنقل.
val navController = findNavController(R.id.nav_host_fragment)
navController.graph = navController.createGraph(
startDestination = Home
) {
fragment<HomeFragment, Home> {
label = resources.getString(R.string.home_title)
}
fragment<PlantDetailFragment, PlantDetail> {
label = resources.getString(R.string.plant_detail_title)
}
}
في هذا المثال، يتم تحديد وجهتين مجزأتين باستخدام
fragment()
دالة إنشاء DSL. تتطلب هذه الدالة نوعين من type
الوسيطات
.
أولاً، صف Fragment
توفر واجهة المستخدم لهذه الوجهة. تعيين هذا له نفس تأثير
إعداد السمة android:name
على وجهات الأجزاء المحدّدة
باستخدام XML.
ثانيًا، المسار. ويجب أن يكون هذا النوع قابلاً للتسلسل ويمتد من Any
. أُنشأها جون هنتر، الذي كان متخصصًا
أن يحتوي على أي وسيطات تنقل سيتم استخدامها بواسطة هذه الوجهة،
وأنواعها.
تقبل الدالة أيضًا دالة lambda الاختيارية لإجراء ضبط إضافي، مثل مثل تصنيف الوجهة، وكذلك دوال أداة الإنشاء المضمنة، وسيطات وروابط لمواضع معينة.
التنقّل باستخدام الرسم البياني لـ Kotlin DSL
أخيرًا، يمكنك الانتقال من home
إلى plant_detail
باستخدام
NavController.navigate()
المكالمات:
private fun navigateToPlant(plantId: String) {
findNavController().navigate(route = PlantDetail(id = plantId))
}
في PlantDetailFragment
، يمكنك الحصول على وسيطات التنقل من خلال الحصول على
الحالي
NavBackStackEntry
والاتصال
toRoute
عليه للحصول على مثيل المسار.
val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
في حال استخدام PlantDetailFragment
لـ ViewModel
، يمكنك الحصول على مثيل المسار باستخدام
SavedStateHandle.toRoute
val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
يصف باقي هذا الدليل عناصر الرسم البياني للتنقل الشائعة والوجهات وكيفية استخدامها عند إنشاء الرسم البياني.
الوجهات
يوفر Kotlin DSL دعمًا مدمجًا لثلاثة أنواع من الوجهات:
Fragment
وActivity
وNavGraph
، لكلّ منها وجهتها الخاصة
المتوفرة لإنشاء وتهيئة دالة الامتداد
الوجهة.
وجهات التجزئة
تشير رسالة الأشكال البيانية
fragment()
يمكن تحديد معلمة دالة DSL باستخدام فئة التجزئة لواجهة المستخدم
نوع المسار يُستخدَم لتحديد هذه الوجهة بشكلٍ فريد، متبوعًا بـ lambda
حيث يمكنك توفير تهيئة إضافية كما هو موضح في قسم الانتقال
من خلال قسم Kotlin DSL البياني.
fragment<MyFragment, MyRoute> {
label = getString(R.string.fragment_title)
// custom argument types, deepLinks
}
وجهة النشاط
تشير رسالة الأشكال البيانية
activity()
تأخذ دالة DSL معلَمة نوع للمسار ولكن لا يتم تحديد معلمة إلى
أي فئة نشاط تنفيذ. ويمكنك بدلاً من ذلك ضبط سمة activityClass
اختيارية في
دالة lambda لاحقة. تتيح لك هذه المرونة تحديد وجهة أنشطة
هو نشاط يجب إطلاقه باستخدام دالة ضمنية
intent، عندما تتضمّن
لن يكون لفئة النشاط أي معنى. وكما هو الحال مع الوجهات المجزأة، يمكنك أيضًا
يمكنك ضبط تصنيف ووسيطات مخصصة وروابط لصفحات في التطبيق.
activity<MyRoute> {
label = getString(R.string.activity_title)
// custom argument types, deepLinks...
activityClass = MyActivity::class
}
وجهة الرسم البياني للتنقل
تشير رسالة الأشكال البيانية
navigation()
يمكن استخدام دالة DSL لإنشاء تنقل متداخل
الرسم البياني. تأخذ هذه الدالة نوعًا
للمسار المراد تعيينه لهذا الرسم البياني. يتطلب الأمر أيضًا وسيطتين:
مسار وجهة بداية الرسم البياني، ودالة lambda إلى
تهيئة الرسم البياني. تتضمن العناصر الصالحة وجهات أخرى ووسيطة مخصصة
وأنواع الروابط المؤدية إلى صفحات في التطبيق وتصنيف وصفي
الوجهة.
يمكن أن يكون هذا التصنيف مفيدًا لربط الرسم البياني للتنقل بمكونات واجهة المستخدم باستخدام
NavigationUI
@Serializable data object HomeGraph
@Serializable data object Home
navigation<HomeGraph>(startDestination = Home) {
// label, other destinations, deep links
}
إتاحة الوجهات المخصّصة
في حال استخدام نوع وجهة جديد
التي لا تدعم مباشرة Kotlin DSL، يمكنك إضافة هذه الوجهات
لغة Kotlin DSL باستخدام
addDestination()
:
// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
addDestination(customDestination)
وكبديل، يمكنك أيضًا استخدام عامل التشغيل Unary plus لإضافة قيمة الوجهة المنشأة مباشرةً إلى الرسم البياني:
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
تقديم وسيطات الوجهة
يمكن تحديد وسيطات الوجهة كجزء من فئة المسار. يمكن أن تكون هذه بنفس الطريقة التي تستخدمها مع أي فئة Kotlin. الوسيطات المطلوبة هي أنواعًا غير قابلة للقيم الفارغة والوسيطات الاختيارية يتم تحديد الوسيطات الاختيارية القيم.
الآلية الأساسية لتمثيل المسارات ووسيطاتها هي سلسلة
بناءً. يتيح استخدام السلاسل لوضع نماذج للمسارات تخزين حالة التنقل
تمت استعادتها من القرص أثناء الضبط
التغييرات والعملية التي يبدأها النظام
الموت. لهذا السبب،
يجب أن تكون كل وسيطة تنقل قابلة للتسلسل، أي أنها يجب أن تحتوي على
تُحوِّل التمثيل في الذاكرة لقيمة الوسيطة إلى
String
تسلسل Kotlin
المكوّن الإضافي
تنشئ تلقائيًا طرق تسلسل لـ البيانات الأساسية
البيانات عندما
تمت إضافة تعليق @Serializable
التوضيحي إلى عنصر.
@Serializable
data class MyRoute(
val id: String,
val myList: List<Int>,
val optionalArg: String? = null
)
fragment<MyFragment, MyRoute>
توفير أنواع مخصّصة
بالنسبة إلى أنواع الوسيطات المخصصة، ستحتاج إلى توفير فئة NavType
مخصصة. هذا النمط
تتيح لك التحكم الدقيق في كيفية تحليل النوع من المسار أو رابط لصفحة في التطبيق.
على سبيل المثال، قد يحتوي المسار المستخدم لتحديد شاشة البحث على فئة يمثل معلَمات البحث:
@Serializable
data class SearchRoute(val parameters: SearchParameters)
@Serializable
data class SearchParameters(
val searchQuery: String,
val filters: List<String>
)
يمكن كتابة NavType
مخصّص على النحو التالي:
val SearchParametersType = object : NavType<SearchParameters>(
isNullableAllowed = false
) {
override fun put(bundle: Bundle, key: String, value: SearchParameters) {
bundle.putParcelable(key, value)
}
override fun get(bundle: Bundle, key: String): SearchParameters {
return bundle.getParcelable(key) as SearchParameters
}
override fun serializeAsValue(value: SearchParameters): String {
// Serialized values must always be Uri encoded
return Uri.encode(Json.encodeToString(value))
}
override fun parseValue(value: String): SearchParameters {
// Navigation takes care of decoding the string
// before passing it to parseValue()
return Json.decodeFromString<SearchParameters>(value)
}
}
يمكن بعد ذلك استخدام هذا في Kotlin DSL مثل أي نوع آخر:
fragment<SearchFragment, SearchRoute> {
label = getString(R.string.plant_search_title)
typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
}
عند الانتقال إلى الوجهة، يمكنك إنشاء مثيل لمسارك:
val params = SearchParameters("rose", listOf("available"))
navController.navigate(route = SearchRoute(params))
يمكن الحصول على المَعلمة من المسار في الوجهة:
val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters
روابط لصفحات معيّنة
يمكن إضافة روابط لمواضع معينة إلى أي وجهة، تمامًا كما هو الحال مع الروابط التي تستند إلى XML الرسم البياني للتنقل. كل الإجراءات نفسها المحدّدة في إنشاء رابط لصفحة معيّنة الخاصة بوجهة معيّنة يسري على العملية من إنشاء رابط لموضع معيّن باستخدام Kotlin DSL.
عند إنشاء رابط ضمني لصفحة في التطبيق
ومع ذلك، ليس لديك مورد تنقُّل بتنسيق XML يمكن تحليله من أجل
<deepLink>
. لذلك، لا يمكنك الاعتماد على وضع <nav-graph>
في ملف AndroidManifest.xml
، ويجب إضافة intent بدلاً من ذلك
فلاتر إلى نشاطك يدويًا. الهدف
يجب أن يتطابق الفلتر الذي توفّره مع المسار الأساسي والإجراء ونوع mime
روابط لمواضع معينة لتطبيقك.
تتم إضافة الروابط المؤدية إلى صفحات في التطبيق إلى وجهة من خلال استدعاء الدالة deepLink
داخل
لامدا للوجهة. تقبل المسار كنوع معلمة، و
مَعلمة basePath
للمسار الأساسي لعنوان URL المستخدَم في الرابط لصفحة في التطبيق.
يمكنك أيضًا إضافة إجراء ونوع MIME باستخدام
deepLinkBuilder
لاحقًا لامدا.
ينشئ المثال التالي معرّف موارد منتظم (URI) لرابط صفحة معيّنة في وجهة Home
.
@Serializable data object Home
fragment<HomeFragment, Home>{
deepLink<Home>(basePath = "www.example.com/home"){
// Optionally, specify the action and/or mime type that this destination
// supports
action = "android.intent.action.MY_ACTION"
mimeType = "image/*"
}
}
تنسيق معرّف الموارد المنتظم (URI)
يتم إنشاء تنسيق معرّف الموارد المنتظم (URI) لرابط صفحة معيّنة في التطبيق تلقائيًا من حقول المسار. باستخدام القواعد التالية:
- يتم إلحاق المَعلمات المطلوبة في شكل مَعلمات المسار (مثال:
/{id}
) - المَعلمات ذات القيمة التلقائية (المَعلمات الاختيارية) يتم إلحاقها بالمَعلمة query
المَعلمات (مثال:
?name={name}
) - يتم إلحاق المجموعات باعتبارها مَعلمات طلب البحث (مثال:
?items={value1}&items={value2}
) - يتطابق ترتيب المعلمات مع ترتيب الحقول في المسار
على سبيل المثال، نوع المسار التالي:
@Serializable data class PlantDetail(
val id: String,
val name: String,
val colors: List<String>,
val latinName: String? = null,
)
تم إنشاء تنسيق معرف الموارد المنتظم (URI) لـ:
basePath/{id}/{name}/?colors={color1}&colors={color2}&latinName={latinName}
ما مِن حدّ أقصى لعدد الروابط لصفحات معيّنة التي يمكنك إضافتها. في كل مرة تتصل فيها
deepLink()
يتم إلحاق رابط جديد لصفحة في التطبيق بالقائمة التي يتم الاحتفاظ بها لهذه الوجهة.
القيود
المكوّن الإضافي الوسيطات الآمنة هو
غير متوافق مع Kotlin DSL، حيث يبحث المكون الإضافي عن ملفات موارد XML
إنشاء صفَّين Directions
وArguments