يوفر مكوِّن التنقل لغة خاصة بالنطاق تستند إلى Kotlin،
DSL التي تعتمد على نموذج أمان من نوع Kotlin
البنّائون
. تتيح لك واجهة برمجة التطبيقات هذه إنشاء الرسم البياني بشكل صريح في رمز Kotlin بدلاً من
داخل مورد XML. يمكن أن يكون ذلك مفيدًا إذا كنت تريد إنشاء تنقّل في تطبيقك
بشكل ديناميكي. على سبيل المثال، يمكن لتطبيقك تنزيل ملف
تهيئة التنقل من خدمة ويب خارجية ثم استخدم ذلك
لإنشاء رسم بياني للتنقل بشكل ديناميكي في صفحة
onCreate()
.
التبعيات
لاستخدام Kotlin DSL مع مكونات Fragment، أضِف الملحق التالي إلى ملف
build.gradle
في تطبيقك:
رائع
dependencies { def nav_version = "2.9.2" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.9.2" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
إنشاء رسم بياني
فيما يلي مثال أساسي على نموذج دوار الشمس
التطبيق. لهذا الغرض
على سبيل المثال، لدينا وجهتان: home
وplant_detail
. home
وجود الوجهة عند تشغيل المستخدم للتطبيق لأول مرة. هذه الوجهة
تعرض قائمة بالنباتات من حديقة المستخدم. عندما يحدد المستخدم أحد
النباتات، ينتقل التطبيق إلى وجهة plant_detail
.
يعرض الشكل 1 هذه الوجهات بالإضافة إلى الوسيطات المطلوبة لوجهة
plant_detail
والإجراء to_plant_detail
الذي يستخدمه التطبيق
للتنقّل من home
إلى 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
تستخدم serialized
أنواعًا بدلاً من
المعرفات. يتم تمثيل كل مسار بنوع فريد.
عند التعامل مع الوسيطات، يتم دمجها في نوع المسار . يتيح لك ذلك أمان النوع لوسيطات التنقّل.
@Serializable data object Home
@Serializable data class Plant(val id: String)
إنشاء رسم بياني باستخدام لغة برمجة منتظمة NavGraphBuilder
بعد تحديد المسارات، يمكنك إنشاء رسم بياني للتنقل.
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
، ولكلّ منها
وظيفة إضافة مضمّنة متاحة لإنشاء الوجهة وضبطها.
وجهات التجزئة
يمكن إضافة مَعلمات إلى دالة DSL الخاصة بـ
fragment()
باستخدام فئة المقتطف لواجهة المستخدم و
نوع المسار المستخدَم لتحديد هذه الوجهة بشكل فريد، متبوعًا بلامدا
يمكنك من خلالها تقديم إعدادات إضافية كما هو موضّح في قسم التنقّل
باستخدام الرسم البياني DSL في Kotlin.
fragment<MyFragment, MyRoute> {
label = getString(R.string.fragment_title)
// custom argument types, deepLinks
}
وجهة النشاط
تشير رسالة الأشكال البيانية
activity()
تأخذ دالة DSL معلَمة نوع للمسار ولكن لا يتم تحديد معلمة إلى
أي فئة نشاط تنفيذ. بدلاً من ذلك، يمكنك ضبط activityClass
اختياري في
لامدا متأخرة. تتيح لك هذه المرونة تحديد وجهة أنشطة
هو نشاط يجب إطلاقه باستخدام دالة ضمنية
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
@Parcelize
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>(
typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
) {
label = getString(R.string.plant_search_title)
}
عند الانتقال إلى الوجهة، أنشئ مثيلًا لمسارك:
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 بدلاً من ذلك
فلاتر إلى نشاطك يدويًا. يجب أن يتطابق فلتر القصد
الذي تقدّمه مع المسار الأساسي والإجراء ونوع المحتوى المتعدّد الوسائط
لروابط الصفحات في تطبيقك.
تتم إضافة الروابط المؤدية إلى صفحات في التطبيق إلى وجهة من خلال استدعاء الدالة 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)
يتم إنشاء تنسيق معرّف الموارد المنتظم لرابط صفحة في التطبيق تلقائيًا من حقول المسار باستخدام القواعد التالية:
- يتم إلحاق المَعلمات المطلوبة في شكل مَعلمات المسار (مثال:
/{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()
، تتم إضافة رابط جديد يؤدي إلى صفحة معيّنة إلى قائمة يتم الاحتفاظ بها لتلك الوجهة.
القيود
المكوّن الإضافي Safe Args غير متوافق
مع Kotlin DSL، لأنّ المكوّن الإضافي يبحث عن ملفات موارد XML لأجل
إنشاء فئتَي Directions
وArguments
.