फ़्रैगमेंट और Kotlin DSL

नेविगेशन कॉम्पोनेंट, Kotlin पर आधारित डोमेन के लिए खास भाषा उपलब्ध कराता है या DSL, जो Kotlin के टाइप-सेफ़ बिल्डर को अपनाएं. इस एपीआई की मदद से, एक्सएमएल रिसॉर्स के बजाय, अपने Kotlin कोड में ग्राफ़ को एलान के तौर पर बनाया जा सकता है. यह तब उपयोगी हो सकता है, जब आप अपने ऐप्लिकेशन का नेविगेशन की सुविधा देता है. उदाहरण के लिए, आपका ऐप्लिकेशन किसी बाहरी वेब सेवा से नेविगेशन कॉन्फ़िगरेशन को डाउनलोड और कैश मेमोरी में सेव कर सकता है. इसके बाद, उस कॉन्फ़िगरेशन का इस्तेमाल करके, अपनी गतिविधि के onCreate() फ़ंक्शन में नेविगेशन ग्राफ़ को डाइनैमिक तौर पर बनाया जा सकता है.

डिपेंडेंसी

फ़्रैगमेंट के साथ Kotlin DSL का इस्तेमाल करने के लिए, अपने ऐप्लिकेशन में नीचे दी गई डिपेंडेंसी जोड़ें build.gradle फ़ाइल:

Groovy

dependencies {
    def nav_version = "2.9.3"

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

Kotlin

dependencies {
    val nav_version = "2.9.3"

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

ग्राफ़ बनाना

यहां एक सामान्य उदाहरण दिया गया है, जो सनफ़्लावर ऐप्लिकेशन है. इसके लिए उदाहरण के लिए, हमारे दो डेस्टिनेशन हैं: home और plant_detail. जब उपयोगकर्ता पहली बार ऐप्लिकेशन लॉन्च करता है, तो home डेस्टिनेशन मौजूद होता है. यह डेस्टिनेशन, उपयोगकर्ता के गार्डन में मौजूद पौधों की सूची दिखाता है. जब उपयोगकर्ता इनमें से किसी एक पौधे को चुनता है, तो ऐप्लिकेशन plant_detail डेस्टिनेशन पर नेविगेट करता है.

पहली इमेज में, इन डेस्टिनेशन के साथ-साथ plant_detail डेस्टिनेशन के लिए ज़रूरी आर्ग्युमेंट और ऐक्शन to_plant_detail दिखाया गया है. ऐप्लिकेशन, home से plant_detail पर नेविगेट करने के लिए, to_plant_detail का इस्तेमाल करता है.

Sunflower ऐप्लिकेशन में दो डेस्टिनेशन हैं. साथ ही, एक ऐसी कार्रवाई भी है जो उन्हें आपस में जोड़ती है.
पहली इमेज. Sunflower ऐप्लिकेशन के दो डेस्टिनेशन हैं, home और plant_detail. साथ ही, इस कार्रवाई को उन्हें जोड़ता है.

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() के हिस्से के तौर पर सेट किया जा सके गतिविधि में मौजूद हैं.

एक्सएमएल में, कोई कार्रवाई किसी डेस्टिनेशन आईडी को एक या उससे ज़्यादा आर्ग्युमेंट से जोड़ती है. हालांकि, नेविगेशन डीएसएल का इस्तेमाल करते समय, रास्ते में आर्ग्युमेंट शामिल किए जा सकते हैं. इसका मतलब है कि डीएसएल का इस्तेमाल करते समय, किसी तरह की कार्रवाई करने की ज़रूरत नहीं होती.

अगला चरण, उन रास्तों को तय करना है जिनका इस्तेमाल आप अपने ग्राफ़ को तय करते समय करेंगे.

अपने ग्राफ़ के लिए रास्ते बनाना

एक्सएमएल पर आधारित नेविगेशन ग्राफ़ को Android के बने होने की प्रोसेस के हिस्से के तौर पर पार्स किया जाता है. हर id के लिए, न्यूमेरिक (संख्या वाला) कॉन्स्टेंट बनाया जाता है एट्रिब्यूट की वैल्यू सबमिट करें. बिल्ड टाइम के दौरान जनरेट किए गए ये स्टैटिक आईडी, रनटाइम के दौरान आपका नेविगेशन ग्राफ़ बनाते समय उपलब्ध होता है, ताकि नेविगेशन DSL क्रम से लगाने की सुविधा का इस्तेमाल करता है के बजाय आईडी. हर रास्ता एक खास टाइप से दिखाया जाता है.

आर्ग्युमेंट के साथ काम करते समय, इन्हें रूट में बनाया जाता है type हैं. इससे, नेविगेशन आर्ग्युमेंट के लिए टाइप सेफ़्टी का इस्तेमाल किया जा सकता है.

@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() डीएसएल बिल्डर फ़ंक्शन का इस्तेमाल करके, दो फ़्रैगमेंट डेस्टिनेशन तय किए गए हैं. इस फ़ंक्शन के लिए दो टाइप की ज़रूरत है आर्ग्युमेंट को अपनाएं.

पहला, Fragment क्लास, जो इस डेस्टिनेशन के लिए यूज़र इंटरफ़ेस (यूआई) उपलब्ध कराती है. इसे सेट करने का असर वैसा ही होता है जैसा एक्सएमएल का इस्तेमाल करके तय किए गए फ़्रैगमेंट डेस्टिनेशन पर android:name एट्रिब्यूट सेट करने का होता है.

दूसरा, रास्ता. यह ऐसा होना चाहिए जिसे क्रम से लगाया जा सके और Any से भी आगे बढ़ाया जा सके. यह इसमें ऐसे कोई नेविगेशन आर्ग्युमेंट होने चाहिए जिनका इस्तेमाल इस डेस्टिनेशन में किया जाएगा, और उनके प्रकार.

यह फ़ंक्शन, अतिरिक्त कॉन्फ़िगरेशन के लिए वैकल्पिक लैम्ब्डा भी स्वीकार करता है. जैसे, डेस्टिनेशन लेबल. साथ ही, कस्टम आर्ग्युमेंट और डीप लिंक के लिए एम्बेड किए गए बिल्डर फ़ंक्शन भी स्वीकार करता है.

आखिर में, NavController.navigate() कॉल का इस्तेमाल करके, home से plant_detail पर जाया जा सकता है:

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 डेस्टिनेशन, जिनमें से हर एक की अपनी अलग जगह है इनलाइन एक्सटेंशन फ़ंक्शन, GA4 प्रॉपर्टी को बनाने और कॉन्फ़िगर करने के लिए उपलब्ध है गंतव्य.

फ़्रैगमेंट डेस्टिनेशन

fragment() डीएसएल फ़ंक्शन को यूज़र इंटरफ़ेस (यूआई) के लिए फ़्रैगमेंट क्लास और इस डेस्टिनेशन की खास पहचान करने के लिए इस्तेमाल किए गए रूट टाइप के साथ पैरामीटर किया जा सकता है. इसके बाद, एक लैम्ब्डा फ़ंक्शन दिया जा सकता है, जहां Kotlin डीएसएल ग्राफ़ की मदद से नेविगेट करना सेक्शन में बताए गए तरीके से अतिरिक्त कॉन्फ़िगरेशन दिया जा सकता है.

fragment<MyFragment, MyRoute> {
   label = getString(R.string.fragment_title)
   // custom argument types, deepLinks
}

गतिविधि की जगह

कॉन्टेंट बनाने activity() DSL फ़ंक्शन रूट के लिए एक प्रकार पैरामीटर लेता है, लेकिन उसे लागू करने वाली गतिविधि की कोई भी क्लास शामिल हो सकती है. इसके बजाय, आप वैकल्पिक activityClass को मॉडल को एक्ज़ीक्यूट करने की सुविधा मिलती है. इस सुविधा की मदद से, इनके लिए गतिविधि का डेस्टिनेशन तय किया जा सकता है एक ऐसी गतिविधि जिसे इंप्लिसिट वैल्यू का इस्तेमाल करके लॉन्च किया जाना चाहिए इंटेंट होता है, जिसमें अश्लील गतिविधि क्लास का कोई मतलब नहीं रह जाता. फ़्रैगमेंट डेस्टिनेशन की तरह ही, लेबल, कस्टम आर्ग्युमेंट, और डीप लिंक को भी कॉन्फ़िगर किया जा सकता है.

activity<MyRoute> {
   label = getString(R.string.activity_title)
   // custom argument types, deepLinks...

   activityClass = MyActivity::class
}

नेस्ट किए गए नेविगेशन ग्राफ़ बनाने के लिए, navigation() डीएसएल फ़ंक्शन का इस्तेमाल किया जा सकता है. यह फ़ंक्शन, इस ग्राफ़ को असाइन करने के लिए रास्ते के टाइप पैरामीटर को लेता है. इसमें दो तर्क भी होते हैं: ग्राफ़ के शुरुआती डेस्टिनेशन का रूट है, और लैम्डा को आगे जाने के लिए ग्राफ़ को कॉन्फ़िगर करें. मान्य एलिमेंट में, अन्य डेस्टिनेशन, कस्टम आर्ग्युमेंट टाइप, डीप लिंक, और डेस्टिनेशन के लिए जानकारी देने वाला लेबल शामिल हैं. यह लेबल, नेविगेशन ग्राफ़ को यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट से बाइंड करने में काम का हो सकता है. इसके लिए, नीचे दिए गए तरीके का इस्तेमाल किया जा सकता है NavigationUI.

@Serializable data object HomeGraph
@Serializable data object Home

navigation<HomeGraph>(startDestination = Home) {
   // label, other destinations, deep links
}

कस्टम डेस्टिनेशन के साथ काम करना

अगर किसी ऐसे नए डेस्टिनेशन टाइप का इस्तेमाल किया जा रहा है जो सीधे तौर पर Kotlin DSL के साथ काम नहीं करता, तो addDestination() का इस्तेमाल करके, इन डेस्टिनेशन को अपने Kotlin DSL में जोड़ा जा सकता है:

// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
    route = Graph.CustomDestination.route
}
addDestination(customDestination)

एक विकल्प के रूप में, आप एक नया सीधे ग्राफ़ पर डेस्टिनेशन बनाया गया:

// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
    route = Graph.CustomDestination.route
}

डेस्टिनेशन के आर्ग्युमेंट देना

डेस्टिनेशन आर्ग्युमेंट को रूट क्लास के हिस्से के तौर पर बताया जा सकता है. ये काम किए जा सकते हैं उसी तरह परिभाषित किया है जैसे किसी Kotlin क्लास के लिए किया जाता है. ज़रूरी आर्ग्युमेंट को ऐसे टाइप के तौर पर परिभाषित किया जाता है जिनमें वैल्यू न होने पर गड़बड़ी का मैसेज मिलता है. वहीं, वैकल्पिक आर्ग्युमेंट के लिए डिफ़ॉल्ट वैल्यू तय की जाती है.

रूट और उनके आर्ग्युमेंट को दिखाने का मुख्य तरीका स्ट्रिंग है आधारित. स्ट्रिंग का इस्तेमाल करके रूट को मॉडल करने से, कॉन्फ़िगरेशन में होने वाले बदलावों और सिस्टम की ओर से शुरू की गई प्रोसेस के बंद होने के दौरान, नेविगेशन की स्थिति को सेव और डिस्क से वापस लाया जा सकता है. इस वजह से, हर नेविगेशन आर्ग्युमेंट को सीरियलाइज़ किया जा सकता है. इसका मतलब है कि इसमें ऐसा तरीका होना चाहिए जो आर्ग्युमेंट वैल्यू को मेमोरी में मौजूद String में बदल दे.

जब किसी ऑब्जेक्ट में @Serializable एनोटेशन जोड़ा जाता है, तो Kotlin के लिए, ऑब्जेक्ट को सीरियलाइज़ करने वाला प्लग इन, बुनियादी टाइप के लिए सीरियलाइज़ेशन के तरीके अपने-आप जनरेट करता है.

@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

डीप लिंक

डीप लिंक किसी भी डेस्टिनेशन पर ठीक वैसे ही जोड़े जा सकते हैं जैसे कि एक्सएमएल से होने पर किए जाते हैं नेविगेशन ग्राफ़. डेस्टिनेशन के लिए डीप लिंक बनाना में बताए गए सभी तरीके, Kotlin DSL का इस्तेमाल करके डीप लिंक बनाने की प्रोसेस पर भी लागू होते हैं.

इंप्लिसिट डीप लिंक बनाते समय हालांकि, आपके पास ऐसा एक्सएमएल नेविगेशन रिसॉर्स नहीं है जिसका विश्लेषण किया जा सके <deepLink> एलिमेंट. इसलिए, AndroidManifest.xml फ़ाइल में <nav-graph> एलिमेंट डालने पर भरोसा नहीं किया जा सकता. इसके बजाय, आपको अपनी गतिविधि में मैन्युअल तरीके से इंटेंट फ़िल्टर जोड़ने होंगे. मकसद आपने जो फ़िल्टर दिया है वह इसके बेस पाथ, कार्रवाई, और mimetype से मेल खाना चाहिए आपके ऐप्लिकेशन के डीप लिंक.

डेस्टिनेशन के लैम्ब्डा फ़ंक्शन में deepLink फ़ंक्शन को कॉल करके, डेस्टिनेशन में डीप लिंक जोड़े जाते हैं. यह पैरामीटर के तौर पर रूट को स्वीकार करता है. साथ ही, डीप लिंक के लिए इस्तेमाल किए गए यूआरएल के बेस पाथ के लिए पैरामीटर basePath को स्वीकार करता है.

कोई कार्रवाई और mimetype जोड़ने के लिए, आप इनका इस्तेमाल भी कर सकते हैं: deepLinkBuilder पीछे Lambda फ़ंक्शन.

नीचे दिए गए उदाहरण में, 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/*"
  }
}

यूआरआई फ़ॉर्मैट

डीप लिंक यूआरआई फ़ॉर्मैट, इन नियमों का इस्तेमाल करके, रूट के फ़ील्ड से अपने-आप जनरेट होता है:

  • ज़रूरी पैरामीटर, पाथ पैरामीटर (उदाहरण: /{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,
)

का जनरेट किया गया यूआरआई फ़ॉर्मैट है:

basePath/{id}/{name}/?colors={color1}&colors={color2}&latinName={latinName}

जोड़े जा सकने वाले डीप लिंक की कोई सीमा नहीं है. deepLink() को हर बार कॉल करने पर, उस डेस्टिनेशन के लिए बनाई गई सूची में एक नया डीप लिंक जुड़ जाता है.

पाबंदियां

Safe Args प्लगिन यह Kotlin DSL के साथ काम नहीं करती, क्योंकि प्लगिन इसके लिए एक्सएमएल रिसॉर्स फ़ाइलों को खोजता है Directions और Arguments क्लास जनरेट करें.