টুকরা এবং কোটলিন ডিএসএল

নেভিগেশন উপাদান একটি কোটলিন-ভিত্তিক ডোমেন-নির্দিষ্ট ভাষা, বা ডিএসএল প্রদান করে, যা কোটলিনের টাইপ-সেফ নির্মাতাদের উপর নির্ভর করে। এই API আপনাকে XML রিসোর্সের পরিবর্তে আপনার Kotlin কোডে ঘোষণামূলকভাবে আপনার গ্রাফ রচনা করতে দেয়। আপনি যদি আপনার অ্যাপের নেভিগেশন গতিশীলভাবে তৈরি করতে চান তবে এটি কার্যকর হতে পারে। উদাহরণস্বরূপ, আপনার অ্যাপটি একটি বাহ্যিক ওয়েব পরিষেবা থেকে একটি নেভিগেশন কনফিগারেশন ডাউনলোড এবং ক্যাশে করতে পারে এবং তারপরে আপনার কার্যকলাপের onCreate() ফাংশনে গতিশীলভাবে একটি নেভিগেশন গ্রাফ তৈরি করতে সেই কনফিগারেশনটি ব্যবহার করতে পারে।

নির্ভরতা

ফ্র্যাগমেন্টের সাথে Kotlin DSL ব্যবহার করতে, আপনার অ্যাপের build.gradle ফাইলে নিম্নলিখিত নির্ভরতা যোগ করুন:

গ্রোভি

dependencies {
    def nav_version = "2.8.1"

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

কোটলিন

dependencies {
    val nav_version = "2.8.1"

    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 , একটি ক্রিয়া সহ যা তাদের সংযুক্ত করে।

একটি Kotlin DSL Nav গ্রাফ হোস্টিং

আপনি আপনার অ্যাপের নেভিগেশন গ্রাফ তৈরি করার আগে, গ্রাফটি হোস্ট করার জন্য আপনার একটি জায়গা প্রয়োজন৷ এই উদাহরণটি টুকরা ব্যবহার করে, তাই এটি একটি FragmentContainerView এর ভিতরে একটি NavHostFragment এ গ্রাফটি হোস্ট করে:

<!-- 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 ব্যবহার করার সময় কর্মের কোন ধারণা নেই।

পরবর্তী ধাপ হল আপনার গ্রাফ সংজ্ঞায়িত করার সময় আপনি যে রুটগুলি ব্যবহার করবেন তা সংজ্ঞায়িত করা।

আপনার গ্রাফের জন্য রুট তৈরি করুন

XML-ভিত্তিক নেভিগেশন গ্রাফগুলি অ্যান্ড্রয়েড বিল্ড প্রক্রিয়ার অংশ হিসাবে পার্স করা হয়৷ গ্রাফে সংজ্ঞায়িত প্রতিটি id অ্যাট্রিবিউটের জন্য একটি সাংখ্যিক ধ্রুবক তৈরি করা হয়। রানটাইমে আপনার নেভিগেশন গ্রাফ তৈরি করার সময় এই বিল্ড টাইম জেনারেট করা স্ট্যাটিক আইডি পাওয়া যায় না তাই নেভিগেশন ডিএসএল আইডির পরিবর্তে সিরিয়ালাইজেবল প্রকার ব্যবহার করে। প্রতিটি রুট একটি অনন্য টাইপ দ্বারা প্রতিনিধিত্ব করা হয়.

আর্গুমেন্ট নিয়ে কাজ করার সময়, এগুলি রুট টাইপের মধ্যে তৈরি করা হয়। এটি আপনাকে আপনার নেভিগেশন আর্গুমেন্টের জন্য টাইপ নিরাপত্তা দিতে দেয়।

@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 ক্লাস যা এই গন্তব্যের জন্য UI প্রদান করে। XML ব্যবহার করে সংজ্ঞায়িত ফ্র্যাগমেন্ট গন্তব্যে 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 গন্তব্য, যার প্রত্যেকটির নিজস্ব ইনলাইন এক্সটেনশন ফাংশন রয়েছে যা গন্তব্য নির্মাণ এবং কনফিগার করার জন্য উপলব্ধ।

টুকরো গন্তব্য

fragment() ডিএসএল ফাংশনটি ইউআই-এর জন্য ফ্র্যাগমেন্ট ক্লাস এবং এই গন্তব্যটিকে অনন্যভাবে সনাক্ত করতে ব্যবহৃত রুটের প্রকারের সাথে প্যারামিটারাইজ করা যেতে পারে, তারপরে একটি ল্যাম্বডা যেখানে আপনি আপনার Kotlin DSL গ্রাফ বিভাগে নেভিগেট করার মতো অতিরিক্ত কনফিগারেশন প্রদান করতে পারেন।

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

কার্যকলাপ গন্তব্য

activity() ডিএসএল ফাংশনটি রুটের জন্য একটি টাইপ প্যারামিটার নেয় কিন্তু কোনো বাস্তবায়নকারী কার্যকলাপ শ্রেণিতে প্যারামিটারাইজ করা হয় না। পরিবর্তে, আপনি একটি ট্রেলিং ল্যাম্বডাতে একটি ঐচ্ছিক activityClass সেট করেছেন। এই নমনীয়তা আপনাকে একটি কার্যকলাপের জন্য একটি কার্যকলাপের গন্তব্য সংজ্ঞায়িত করতে দেয় যা একটি অন্তর্নিহিত অভিপ্রায় ব্যবহার করে চালু করা উচিত, যেখানে একটি সুস্পষ্ট কার্যকলাপ শ্রেণির অর্থ হবে না। টুকরো গন্তব্যগুলির মতো, আপনি একটি লেবেল, কাস্টম আর্গুমেন্ট এবং গভীর লিঙ্কগুলিও কনফিগার করতে পারেন।

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

   activityClass = MyActivity::class
}

navigation() ডিএসএল ফাংশনটি নেস্টেড নেভিগেশন গ্রাফ তৈরি করতে ব্যবহার করা যেতে পারে। এই ফাংশনটি এই গ্রাফে বরাদ্দ করার জন্য রুটের জন্য একটি টাইপ প্যারামিটার নেয়। এটি দুটি আর্গুমেন্টও নেয়: গ্রাফের শুরুর গন্তব্যের রুট এবং গ্রাফটিকে আরও কনফিগার করার জন্য একটি ল্যাম্বডা। বৈধ উপাদানগুলির মধ্যে অন্যান্য গন্তব্য, কাস্টম আর্গুমেন্টের ধরন, গভীর লিঙ্ক এবং গন্তব্যের জন্য একটি বর্ণনামূলক লেবেল অন্তর্ভুক্ত রয়েছে। এই লেবেলটি NavigationUI ব্যবহার করে UI উপাদানগুলিতে নেভিগেশন গ্রাফ বাঁধাই করার জন্য উপযোগী হতে পারে।

@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)

একটি বিকল্প হিসাবে, আপনি সরাসরি গ্রাফে একটি নতুন নির্মিত গন্তব্য যোগ করতে unary plus অপারেটর ব্যবহার করতে পারেন:

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

গন্তব্য আর্গুমেন্ট প্রদান

গন্তব্য আর্গুমেন্টকে রুট ক্লাসের অংশ হিসাবে সংজ্ঞায়িত করা যেতে পারে। এগুলিকে আপনি যেকোন কোটলিন ক্লাসের জন্য একইভাবে সংজ্ঞায়িত করতে পারেন। প্রয়োজনীয় আর্গুমেন্টগুলিকে অ-নূলযোগ্য প্রকার হিসাবে সংজ্ঞায়িত করা হয় এবং ঐচ্ছিক আর্গুমেন্টগুলি ডিফল্ট মানগুলির সাথে সংজ্ঞায়িত করা হয়।

রুট এবং তাদের আর্গুমেন্ট উপস্থাপনের জন্য অন্তর্নিহিত প্রক্রিয়া হল স্ট্রিং ভিত্তিক। মডেল রুটগুলিতে স্ট্রিং ব্যবহার করে কনফিগারেশন পরিবর্তন এবং সিস্টেম-সূচিত প্রক্রিয়া মৃত্যুর সময় ডিস্ক থেকে নেভিগেশন স্থিতি সংরক্ষণ এবং পুনরুদ্ধার করার অনুমতি দেয়। এই কারণে, প্রতিটি নেভিগেশন আর্গুমেন্টকে সিরিয়ালাইজেবল হতে হবে, অর্থাৎ, এটিতে এমন একটি পদ্ধতি থাকা উচিত যা আর্গুমেন্ট মানের ইন-মেমরি উপস্থাপনাকে একটি String -এ রূপান্তরিত করে।

কোটলিন সিরিয়ালাইজেশন প্লাগইন স্বয়ংক্রিয়ভাবে মৌলিক প্রকারের জন্য সিরিয়ালাইজেশন পদ্ধতি তৈরি করে যখন কোনো বস্তুতে @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)
  }
}

এটি তখন আপনার কোটলিন ডিএসএল-এ অন্য যেকোন প্রকারের মতো ব্যবহার করা যেতে পারে:

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 চালিত নেভিগেশন গ্রাফের সাথে করতে পারে। একটি গন্তব্যের জন্য একটি গভীর লিঙ্ক তৈরিতে সংজ্ঞায়িত একই পদ্ধতিগুলি কোটলিন ডিএসএল ব্যবহার করে একটি গভীর লিঙ্ক তৈরি করার প্রক্রিয়াতে প্রযোজ্য।

যদিও একটি অন্তর্নিহিত গভীর লিঙ্ক তৈরি করার সময়, আপনার কাছে একটি XML নেভিগেশন সংস্থান নেই যা <deepLink> উপাদানগুলির জন্য বিশ্লেষণ করা যেতে পারে। অতএব, আপনি আপনার AndroidManifest.xml ফাইলে একটি <nav-graph> উপাদান রাখার উপর নির্ভর করতে পারবেন না এবং পরিবর্তে আপনার কার্যকলাপে ম্যানুয়ালি উদ্দেশ্য ফিল্টার যোগ করতে হবে। আপনার সরবরাহ করা অভিপ্রায় ফিল্টারটি আপনার অ্যাপের গভীর লিঙ্কগুলির বেস পাথ, অ্যাকশন এবং মাইমেটাইপের সাথে মেলে।

গন্তব্যের ল্যাম্বডার ভিতরে deepLink ফাংশনকে কল করে একটি গন্তব্যে গভীর লিঙ্ক যোগ করা হয়। এটি রুটটিকে একটি প্যারামিটারাইজড টাইপ হিসাবে গ্রহণ করে এবং গভীর লিঙ্কের জন্য ব্যবহৃত URL-এর বেস পাথের জন্য একটি প্যারামিটার basePath

আপনি deepLinkBuilder trailing 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/*"
  }
}

URI বিন্যাস

গভীর লিঙ্ক URI বিন্যাস স্বয়ংক্রিয়ভাবে রুটের ক্ষেত্রগুলি থেকে নিম্নলিখিত নিয়মগুলি ব্যবহার করে তৈরি হয়:

  • প্রয়োজনীয় প্যারামিটারগুলি পাথ প্যারামিটার হিসাবে যুক্ত করা হয়েছে (উদাহরণ: /{id} )
  • একটি ডিফল্ট মান (ঐচ্ছিক পরামিতি) সহ প্যারামিটারগুলি কোয়েরি প্যারামিটার হিসাবে যুক্ত করা হয় (উদাহরণ: ?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() কল করেন একটি নতুন ডিপ লিঙ্ক একটি তালিকায় যুক্ত করা হয় যা সেই গন্তব্যের জন্য রক্ষণাবেক্ষণ করা হয়।

সীমাবদ্ধতা

সেফ আর্গস প্লাগইনটি কোটলিন ডিএসএল-এর সাথে বেমানান, কারণ প্লাগইনটি Directions এবং Arguments ক্লাস তৈরি করার জন্য XML রিসোর্স ফাইলের সন্ধান করে।