Parçalar ve Kotlin DSL

Gezinme bileşeni, Kotlin'in tür güvenliğine sahip oluşturucularına dayanan Kotlin tabanlı bir alana özgü dil veya DSL sağlar. Bu API, grafiğinizi bildirerek oluşturmak yerine Kotlin kodunuzda oluşturmanıza olanak tanır daha kolay olur. Bu, uygulamanızın özel bir kitle oluşturmak gezinmeyi kolaylaştırır. Örneğin, uygulamanız bir harici bir web hizmetinden gezinme yapılandırmasını dinamik olarak bir gezinme grafiği oluşturmak için onCreate() işlevi.

Bağımlılıklar

Kotlin DSL'yi Parçalarla birlikte kullanmak için aşağıdaki bağımlılığı uygulamanızın build.gradle dosyası:

Groovy

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")
}

Grafik oluşturma

Burada, Google Ads kurallarından yola çıkarak hazırlanan Sunflower uygulamasında bulabilirsiniz. Bunun için Örneğin, iki hedefimiz var: home ve plant_detail. home Hedefi, kullanıcı uygulamayı ilk kez başlattığında mevcuttur. Bu hedef, kullanıcının bahçesindeki bitkilerin listesini gösterir. Kullanıcı bitkilerden birini seçtiğinde uygulama plant_detail hedefine gider.

Şekil 1'de bu hedefler, Uygulamanın kullandığı plant_detail hedef ve bir işlem (to_plant_detail) home - plant_detail arası rotayı izleyin.

Sunflower uygulamasında, iki hedefin yanı sıra bu hedefleri birbirine bağlayan bir işlem vardır.
Şekil 1. Sunflower uygulamasının iki hedefi vardır: home ve plant_detail ile birlikte şu işlemi gerçekleştiren bir işlem: birbirine bağlar.

Kotlin DSL gezinme grafiği barındırma

Uygulamanızın gezinme grafiğini oluşturabilmek için öncelikle grafiğe dönüştürülebilir. Bu örnekte parçalar kullanıldığı için, grafiği NavHostFragment bir 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>

Bu örnekte app:navGraph özelliğinin ayarlanmadığına dikkat edin. Grafik bir kaynak olarak tanımlanmadığından res/navigation klasörü olduğundan onCreate() öğesinin bir parçası olarak ayarlanması gerekir bu etkinliği takip etmeniz gerekir.

XML'de, bir işlem hedef kimliğini bir veya daha fazla bağımsız değişkenle birbirine bağlar. Ancak, Navigasyon DSL'sini kullanırken bir rota, yol gösterir. Bu, DSL kullanılırken işlem kavramının olmadığı anlamına gelir.

Bir sonraki adım, işletme hedefinizi tanımlarken kullanacağınız rotaları grafiğe dönüştürülebilir.

Grafiğiniz için rotalar oluşturma

XML tabanlı gezinme grafikleri, bir üründen diğerine adımına geçelim. Her id için sayısal bir sabit sayı oluşturulur özelliğinin değeri. Derleme zamanında oluşturulan bu statik kimlikler Böylece, Gezinme DSL'sini kullanmak için çalışma zamanında gezinme grafiğinizi oluştururken kullanabilirsiniz. seri girilebilir veri türünü kullanır. türler yerine Kimlikler. Her rota, benzersiz bir türle temsil edilir.

Bağımsız değişkenlerle çalışırken, bunlar rotaya eklenir tür'ü seçin. Bu sayede, gezinme bağımsız değişkenleriniz için tür güvenliğine sahip olabilirsiniz.

@Serializable data object Home
@Serializable data class Plant(val id: String)

Güzergahlarınızı tanımladıktan sonra, gezinme grafiğini oluşturabilirsiniz.

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

Bu örnekte, iki parçalı hedef fragment() DSL oluşturucu işlevi. Bu işlev, iki tür gerektirir bağımsız değişkenler ,

Önce bir Fragment sınıfı API'yi kullanabilirsiniz. Bunu ayarlamak tanımlanmış parça hedeflerde android:name özelliğini ayarlayarak anlamına gelir.

İkinci olarak rota. Bu, Any tarihinden itibaren seri hale getirilebilir bir tür olmalıdır. Google bu hedef tarafından kullanılacak gezinme bağımsız değişkenlerini içermelidir. ve türlerini seçin.

İşlev, hedef etiketi gibi ek yapılandırma için isteğe bağlı bir lambda'yı ve özel bağımsız değişkenler ile derin bağlantılar için yerleşik oluşturucu işlevlerini de kabul eder.

Son olarak, NavController.navigate() çağrılarını kullanarak home'ten plant_detail'a gidebilirsiniz:

private fun navigateToPlant(plantId: String) {
   findNavController().navigate(route = PlantDetail(id = plantId))
}

PlantDetailFragment işlevinde, geçerli NavBackStackEntry ve sesli arama yapıyorum toRoute çift tıklayın.

val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id

PlantDetailFragment, ViewModel kullanıyorsa rota örneğini şunu kullanarak alın: SavedStateHandle.toRoute.

val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id

Bu kılavuzun geri kalanında, yaygın olarak kullanılan gezinme grafiği öğeleri, hedefler, bunları nasıl kullanacağınızı öğreneceksiniz.

Hedefler

Kotlin DSL, üç hedef türü için yerleşik destek sağlar: Her biri kendine ait olan Fragment, Activity ve NavGraph hedefleri oluşturmak ve yapılandırmak için kullanılabilen satır içi uzantı işlevi seçeceğiz.

Parça hedefleri

İlgili içeriği oluşturmak için kullanılan fragment() DSL işlevi, kullanıcı arayüzünün parça sınıfı ve bu hedefi benzersiz şekilde tanımlamak için kullanılan rota türü ve ardından bir lambda Burada açıklandığı gibi ek yapılandırma sağlayabileceğiniz yöntem Kotlin DSL grafiğiniz bölümünde görünür.

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

Etkinlik hedefi

activity() DSL işlevi, rota için bir tür parametresi alır ancak herhangi bir uygulayıcı etkinlik sınıfı için parametrelendirilmez. Bunun yerine, anahtar kelimeler için isteğe bağlı bir activityClass bir lambda var. Bu esneklik, açık bir etkinlik sınıfının anlamlı olmadığı durumlarda örtülü intent kullanılarak başlatılması gereken bir etkinlik için etkinlik hedefi tanımlamanıza olanak tanır. Parça hedeflerde olduğu gibi, Etiket, özel bağımsız değişkenler ve derin bağlantılar yapılandırabilirsiniz.

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

   activityClass = MyActivity::class
}

navigation() DSL işlevi, iç içe yerleştirilmiş bir gezinme grafiği oluşturmak için kullanılabilir. Bu işlev bir tür alır parametresini kullanabilirsiniz. Ayrıca iki bağımsız değişken alır: grafiğin başlangıç hedefinin rotası ve grafiği daha da yapılandırmak için bir lambda. Geçerli öğeler arasında diğer hedefler, özel bağımsız değişken yer alır derin bağlantılar ve bu sayfa için açıklayıcı bir etiket hedef. Bu etiket, NavigationUI kullanılarak gezinme grafiğinin kullanıcı arayüzü bileşenlerine bağlanması için yararlı olabilir.

@Serializable data object HomeGraph
@Serializable data object Home

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

Özel hedefleri destekleme

Kotlin DSL'yi doğrudan desteklemeyen bir yeni hedef türü kullanıyorsanız bu hedefleri addDestination() kullanarak Kotlin DSL'nize ekleyebilirsiniz:

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

Alternatif olarak, yeni oluşturulan bir hedefi doğrudan grafiğe eklemek için tek adımlı artı operatörünü de kullanabilirsiniz:

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

Hedef bağımsız değişkenleri sağlama

Hedef bağımsız değişkenleri, rota sınıfının bir parçası olarak tanımlanabilir. Bunlar, herhangi bir Kotlin sınıfı için belirtilen şekilde tanımlanabilir. Gerekli bağımsız değişkenler boş değerli olmayan türler olarak tanımlanır ve isteğe bağlı bağımsız değişkenler varsayılan olarak tanımlanır değerler.

Rotaları ve bunların bağımsız değişkenlerini temsil eden temel mekanizma dize tabanlı. Rotaları modellemek için dize kullanmak, gezinme durumunun depolanmasını ve yapılandırma sırasında diskten geri yüklendi sistem tarafından başlatılan ve işlem ölüm ile ilişkilendirilebilir. İşte bu nedenle Her gezinme bağımsız değişkeni serileştirilebilir olmalıdır; yani, bağımsız değişken değerinin bellek içi gösterimini String

Kotlin serileştirmesi eklenti Google Analytics 4'teki temel alan anahtar kelimeler @Serializable ek açıklaması bir nesneye eklendi.

@Serializable
data class MyRoute(
  val id: String,
  val myList: List<Int>,
  val optionalArg: String? = null
)

fragment<MyFragment, MyRoute>

Özel türler sağlama

Özel bağımsız değişken türleri için özel bir NavType sınıfı sağlamanız gerekir. Bu, türünüzün bir rotadan veya derin bağlantıdan tam olarak nasıl ayrıştırılacağını kontrol etmenizi sağlar.

Örneğin, bir arama ekranını tanımlamak için kullanılan bir rota, arama parametrelerini temsil eden bir sınıf içerebilir:

@Serializable
data class SearchRoute(val parameters: SearchParameters)

@Serializable
@Parcelize
data class SearchParameters(
  val searchQuery: String,
  val filters: List<String>
)

Özel bir NavType şu şekilde yazılabilir:

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

Bu, daha sonra Kotlin DSL'nizde diğer türler gibi kullanılabilir:

fragment<SearchFragment, SearchRoute>(
    typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
) {
    label = getString(R.string.plant_search_title)
}

Hedefe giderken rotanızın bir örneğini oluşturun:

val params = SearchParameters("rose", listOf("available"))
navController.navigate(route = SearchRoute(params))

Parametre, hedefteki rotadan edinilebilir:

val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters

Derin bağlantılar

Derin bağlantılar, XML destekli bir gezinme grafiğinde olduğu gibi herhangi bir hedefe eklenebilir. Derin bağlantı oluşturma bölümünde belirtilen prosedürlerin tümü sürece uygulanır. bir derin bağlantı oluşturmayı öğreneceksiniz.

Dolaylı derin bağlantı oluştururken ancak o zaman hangisi için analiz edilebilecek bir XML gezinme kaynağınız yoksa <deepLink> öğeleri. Bu nedenle, <nav-graph> öğesi; AndroidManifest.xml dosyanıza intent filtreler ekleyebilirsiniz. Amaç sağladığınız filtrenin temel yolu, işlemi ve mime türüyle uygulamanızın derin bağlantılarını kullanın.

Derin bağlantılar, hedefin lambda işlevi içinde deepLink işlevi çağrılarak hedefe eklenir. Rota, parametrelenmiş bir tür olarak kabul edilir ve derin bağlantı için kullanılan URL'nin temel yolu için bir parametre basePath kabul eder.

Ayrıca, deepLinkBuilder lambda var.

Aşağıdaki örnek, Home hedefi için bir derin bağlantı URI'si oluşturmaktadır.

@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 biçimi

Derin bağlantı URI biçimi, aşağıdaki kurallar kullanılarak rotanın alanlarından otomatik olarak oluşturulur:

  • Gerekli parametreler yol parametreleri olarak eklenmiştir (örnek: /{id})
  • Varsayılan değeri olan parametreler (isteğe bağlı parametreler) sorgu parametresi olarak eklenir (örnek: ?name={name})
  • Koleksiyonlar sorgu parametresi olarak eklenir (örnek: ?items={value1}&items={value2})
  • Parametrelerin sırası, rotadaki alanların sıralamasıyla eşleşir

Örneğin, aşağıdaki rota türü:

@Serializable data class PlantDetail(
  val id: String,
  val name: String,
  val colors: List<String>,
  val latinName: String? = null,
)

Oluşturulan URI biçimi şu şekildedir:

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

Ekleyebileceğiniz derin bağlantı sayısıyla ilgili bir sınır yoktur. deepLink() işlevini her çağırdığınızda, söz konusu hedef için tutulan bir listeye yeni bir derin bağlantı eklenir.

Sınırlamalar

Safe Args eklentisi, Directions ve Arguments sınıfları oluşturmak için XML kaynak dosyalarını aradığından Kotlin DSL ile uyumlu değildir.