التجزئة وKotlin DSL

يوفر مكوِّن التنقل لغة خاصة بالنطاق تستند إلى Kotlin، DSL التي تعتمد على نموذج أمان من نوع Kotlin البنّائون . تتيح لك واجهة برمجة التطبيقات هذه إنشاء الرسم البياني بشكل صريح في رمز Kotlin، بدلاً من من داخل مورد XML. وقد يكون هذا مفيدًا إذا كنت تريد إنشاء تصميم، التنقل بشكل ديناميكي. على سبيل المثال، يمكن لتطبيقك تنزيل ملف تهيئة التنقل من خدمة ويب خارجية ثم استخدم ذلك لإنشاء رسم بياني للتنقل بشكل ديناميكي في صفحة onCreate().

التبعيات

لاستخدام Kotlin DSL مع تجزئة، أضف التبعية التالية إلى ملف ملف build.gradle:

رائع

dependencies {
    def nav_version = "2.8.4"

    api "androidx.navigation:navigation-fragment-ktx:$nav_version"
}

Kotlin

dependencies {
    val nav_version = "2.8.4"

    api("androidx.navigation:navigation-fragment-ktx:$nav_version")
}

إنشاء رسم بياني

فيما يلي مثال أساسي على نموذج دوار الشمس التطبيق. لهذا الغرض على سبيل المثال، لدينا وجهتان: home وplant_detail. home وجود الوجهة عند تشغيل المستخدم للتطبيق لأول مرة. هذه الوجهة تعرض قائمة بالنباتات من حديقة المستخدم. عندما يحدد المستخدم أحد النباتات، ينتقل التطبيق إلى وجهة plant_detail.

يوضح الشكل 1 هذه الوجهات مع الوسيطات المطلوبة بواسطة وجهة واحدة (plant_detail) وإجراء، to_plant_detail، يستخدمه التطبيق الانتقال من home إلى plant_detail.

يحتوي تطبيق دوار الشمس على وجهتين إلى جانب إجراء
            تربطهما معًا.
الشكل 1. هناك وجهتان لتطبيق دوار الشمس، 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)

بعد تحديد المسارات، يمكنك إنشاء رسم بياني للتنقل.

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 الاختيارية لإجراء ضبط إضافي، مثل مثل تصنيف الوجهة، وكذلك دوال أداة الإنشاء المضمنة، وسيطات وروابط لمواضع معينة.

أخيرًا، يمكنك الانتقال من 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