Kotlin DSL'yi kullanarak programatik olarak bir grafik oluşturun

Gezinme bileşeni, Kotlin tabanlı alana özgü bir dil sağlar veya DSL, Kotlin'in type-safe derleyiciler oluşturun. Bu API, grafiğinizi Kotlin kodunuzda bildirimli bir şekilde oluşturmanıza olanak tanır. bir XML kaynağı kullanmaktır. Bu, bir proje başlatma belgesi için gezinmeyi kolaylaştırır. Örneğin, uygulamanız harici bir web hizmetinden gezinme yapılandırmasını önbelleğe almak ve ardından dinamik olarak bir gezinme grafiği oluşturmak için bu yapılandırmayı onCreate() işlevi.

Bağımlılıklar

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

Eski

dependencies {
    def nav_version = "2.7.7"

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

Kotlin

dependencies {
    val nav_version = "2.7.7"

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

Grafik oluşturma

Temel bir örnekle, Sunflower uygulaması. Bunun için Örneğin, iki hedefimiz var: home ve plant_detail. home Hedef, kullanıcı uygulamayı ilk kez başlattığında mevcuttur. Bu hedef kullanıcının bahçesindeki bitkilerin bir listesini görüntüler. Kullanıcı bitkiler olduğunda uygulama plant_detail hedefine gidiyor.

Ş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ının iki hedefi ve
            şekilde bağdaştırı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 Nav Grafiği Barındırma

Uygulamanızın gezinme grafiğini oluşturabilmeniz için öncelikle görebilirsiniz. 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 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. Bununla birlikte, Navigasyon DSL'sini kullanırken bir rota, görebilirsiniz. Yani DSL'yi kullanırken işlem kavramı yoktur.

Bir sonraki adım, görüntüleyebilirsiniz.

Grafiğiniz için sabit değerler oluşturun

XML tabanlı gezinme grafikleri Android derleme işleminin bir parçası olarak ayrıştırılır. Sayısal bir sabit değer oluşturulur (grafikte tanımlanan her id özelliği için) Bu derleme zamanı, statik Çalışma zamanında gezinme grafiğinizi oluştururken kimlikler kullanılamaz. Bu nedenle Gezinme DSL'si, kimlikler yerine rota dizelerini kullanır. Her rota benzersiz bir dizedir. Daha az veri almak için bunları sabit değer olarak tanımlamak, risklerini tekrar gözden geçirelim.

Bağımsız argümanlar üzerinde çalışırken rota dizesinde yerleşik olarak bulunur. Rotada bu mantığı oluşturmak, proje boyunca çok sayıda yazım hatası oluşmuştur.

object nav_routes {
    const val home = "home"
    const val plant_detail = "plant_detail"
}

object nav_arguments {
    const val plant_id = "plant_id"
    const val plant_name = "plant_name"
}

Sabit değerlerinizi tanımladıktan sonra, gezinme grafiğe dönüştürülebilir.

val navController = findNavController(R.id.nav_host_fragment)
navController.graph = navController.createGraph(
    startDestination = nav_routes.home
) {
    fragment<HomeFragment>(nav_routes.home) {
        label = resources.getString(R.string.home_title)
    }

    fragment<PlantDetailFragment>("${nav_routes.plant_detail}/{${nav_arguments.plant_id}}") {
        label = resources.getString(R.string.plant_detail_title)
        argument(nav_arguments.plant_id) {
            type = NavType.StringType
        }
    }
}

Bu örnekte, takip eden lambda fragment() DSL oluşturucu işlevi. Bu işlev, hedef için bir rota dizesi gerektiriyor elde edilir. İşlev, lambda ek yapılandırma için. bağımsız değişkenler ve derin bağlantılar için yerleştirilmiş oluşturucu işlevleri.

Şu durumda olan Fragment sınıfı her hedefin kullanıcı arayüzünü, içeride parametreli bir tür olarak iletildiğini yönetir açılı ayraçlar (<>). Bu, android:name öğesinin ayarlanmasıyla aynı etkiye sahiptir özelliğini kullanmanızı öneririz.

Son olarak, standart URL'yi kullanarak home konumundan plant_detail konumuna gidebilirsiniz NavController.Navigation() şunu arar:

private fun navigateToPlant(plantId: String) {
   findNavController().navigate("${nav_routes.plant_detail}/$plantId")
}

PlantDetailFragment işlevinde, bağımsız değişkenin değerini aşağıda gösterildiği gibi alabilirsiniz: aşağıdaki örnekte:

val plantId: String? = arguments?.getString(nav_arguments.plant_id)

Gezinme sırasında bağımsız değişkenlerin nasıl sağlanacağına ilişkin ayrıntıları hedef bağımsız değişkenler sağlama bölümüne bakın.

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, uygulayan parça sınıfına parametre haline getirilebilir ve bu hedefe atanacak benzersiz rota dizesi ve ardından, bölümünde açıklandığı gibi ek yapılandırma sağlayabilirsiniz. Kotlin DSL grafiğinizle gezinme bölümüne bakın.

fragment<FragmentDestination>(nav_routes.route_name) {
   label = getString(R.string.fragment_title)
   // arguments, deepLinks
}

Aktivite hedefi

activity() DSL işlevi, bu hedefe atamak için benzersiz bir rota dizesi alır ancak uygulanan herhangi bir etkinlik sınıfına parametre haline getirilmemiştir. Bunun yerine sondaki lambda içinde isteğe bağlı activityClass Bu esneklik sayesinde implicit intent, açıklamanız gerekmez. Parça hedeflerde olduğu gibi Etiket, bağımsız değişkenler ve derin bağlantılar da yapılandırabilirsiniz.

activity(nav_routes.route_name) {
   label = getString(R.string.activity_title)
   // arguments, deepLinks...

   activityClass = ActivityDestination::class
}

navigation() DSL işlevi, iç içe yerleştirilmiş gezinme grafiği. Bu işlev üç bağımsız değişken alır: grafiğe, grafiğin başlangıç hedefinin rotasını ve lambda'yı kullanarak grafiği daha fazla yapılandırın. Geçerli öğeler arasında diğer hedefler, argümanları, derin bağlantıları ve hedefe ilişkin açıklayıcı etiket. Bu etiket, gezinme grafiğini kullanıcı arayüzüne bağlamak için yararlı olabilir bileşenleri kullanarak Gezinme Arayüzü

navigation("route_to_this_graph", nav_routes.home) {
   // label, other destinations, deep links
}

Özel hedefleri destekleme

Bir yeni hedef türünü Kotlin DSL'yi doğrudan desteklemesi durumunda, bu hedefleri Kotlin'inize ekleyebilirsiniz addDestination() ile DSL:

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

Alternatif olarak, tekli artı operatörünü yeni bir oluşturulan hedefi grafiğe doğrudan ekleyin:

// 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

Herhangi bir hedef, isteğe bağlı veya gerekli bağımsız değişkenleri tanımlayabilir. Eylemler kullanılarak tanımlanabilir argument() tüm etkinlikler için temel sınıf olan NavDestinationBuilder üzerinde hedef oluşturucu türleri. Bu işlev, bağımsız değişkenin adını bir dize olarak alır oluşturmak ve yapılandırmak için kullanılan lambda NavArgument.

Lambda içinde bağımsız değişken veri türünü belirtebilirsiniz. Bu tür durumlarda varsayılan değer, geçerli ve null olup olmadığı gibi ayarlar.

fragment<PlantDetailFragment>("${nav_routes.plant_detail}/{${nav_arguments.plant_id}}") {
    label = getString(R.string.plant_details_title)
    argument(nav_arguments.plant_id) {
        type = NavType.StringType
        defaultValue = getString(R.string.default_plant_id)
        nullable = true  // default false
    }
}

Bir defaultValue sağlanırsa tür tahmin edilebilir. Hem defaultValue hem de ve bir type sağlanmışsa türlerin eşleşmesi gerekir. Bkz. Şu sorgu için NavType referans dokümanları: mevcut bağımsız değişken türlerinin tam listesi.

Özel türler sağlama

Belirli türler, örneğin ParcelableType ve SerializableType, rotalar veya derin bağlantılar tarafından kullanılan dizelerden değerlerin ayrıştırılmasını desteklemez. Bunun nedeni, çalışma zamanında yansımaya dayalı olmamalarıdır. Bir özel rapor sağlayarak NavType sınıfını seçerseniz türünüzün bir rotadan tam olarak nasıl ayrıştırılacağını kontrol edebilirsiniz veya derin bağlantı. Bu sayede Kotlin Serileştirme veya diğer kitaplıklarını kullanabilirsiniz.

Örneğin, arama ekranı hem Serializable (yeni bir URL parametresi sağlamak için) kodlama/kod çözme desteği) ve Parcelize (verilerin kaydedilmesini ve geri yüklenmesini desteklemek için Bundle üzerinden):

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

Özel NavType şöyle 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>(nav_routes.plant_search) {
    label = getString(R.string.plant_search_title)
    argument(nav_arguments.search_parameters) {
        type = SearchParametersType
        defaultValue = SearchParameters("cactus", emptyList())
    }
}

NavType, her bir alanın hem yazma hem de okuma sürecini içerir ve bu da anlamına gelir. Bu, şuraya gittiğinizde de NavType öğesinin kullanılması gerektiği anlamına gelir: hedeften emin olmanız gerekir:

val params = SearchParameters("rose", listOf("available"))
val searchArgument = SearchParametersType.serializeAsValue(params)
navController.navigate("${nav_routes.plant_search}/$searchArgument")

Parametre, hedefteki bağımsız değişkenlerden edinilebilir:

val params: SearchParameters? = arguments?.getParcelable(nav_arguments.search_parameters)

Derin bağlantılar

Derin bağlantılar, XML ile desteklenen herhangi bir hedefe eklenebilir. gezinme grafiğidir. Bu belgede tanımlanan prosedürlerin hepsi, Bir hedef için derin bağlantı oluşturma proje yaşam döngüsü boyunca açık derin bağlantıyı kullanmak için Kotlin DSL.

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 niyet filtrelerini manuel olarak etkinleştirin. Sağladığınız amaç filtresi, temel URL modeli, işlemi ve Uygulamanızın derin bağlantılarının mime türünü.

Her bir derin bağlantılı için daha spesifik bir deeplink sağlayabilirsiniz hedefini deepLink() DSL işlevi. Bu işlev, String içeren bir NavDeepLink kabul eder URI modelini, intent işlemlerini temsil eden bir String ve bir mimeType'ı temsil eden String .

Örnek:

deepLink {
    uriPattern = "http://www.example.com/plants/"
    action = "android.intent.action.MY_ACTION"
    mimeType = "image/*"
}

Ekleyebileceğiniz derin bağlantı sayısıyla ilgili bir sınır yoktur. Her aradığınızda deepLink() o hedef için tutulan listeye yeni bir derin bağlantı eklenir.

Yol ve yol bilgilerini de tanımlayan daha karmaşık bir örtülü derin bağlantı senaryosu sorgu tabanlı parametreler aşağıda gösterilmiştir:

val baseUri = "http://www.example.com/plants"

fragment<PlantDetailFragment>(nav_routes.plant_detail) {
   label = getString(R.string.plant_details_title)
   deepLink(navDeepLink {
    uriPattern = "${baseUri}/{id}"
   })
   deepLink(navDeepLink {
    uriPattern = "${baseUri}/{id}?name={plant_name}"
   })
}

Tekliflerinizi otomatikleştirmek ve optimize etmek için dize interpolasyonu tanımlamayı basitleştirir.

Sınırlamalar

Güvenli Bağımsız Değişkenler eklentisi Kotlin DSL ile uyumlu değildir. Bunun nedeni, eklentinin Directions ve Arguments sınıflarını oluşturabilirsiniz.

Daha fazla bilgi

Navigasyon türü olarak güvenlik bölümünü inceleyin Kotlin DSL'niz için tür güvenliğini nasıl sağlayacağınızı ve Gezinme Oluşturma Oluşturma kodu.