Gezinme bileşeni, Kotlin'in tip güvenli derleyicilerini kullanan, Kotlin tabanlı alana özgü bir dil (DSL) sağlar. Bu API, grafiğinizi XML kaynağı yerine Kotlin kodunuzda bildirimli olarak oluşturmanıza olanak tanır. Bu, uygulamanızın gezinme yapısını dinamik olarak oluşturmak istiyorsanız yararlı olabilir. Örneğin, uygulamanız harici bir web hizmetinden gezinme yapılandırması indirip önbelleğe alabilir ve ardından bu yapılandırmayı kullanarak etkinliğinizin onCreate() işlevinde dinamik olarak bir gezinme grafiği oluşturabilir.
Bağımlılıklar
Kotlin DSL'yi parçalarla kullanmak için uygulamanızın build.gradle dosyasına aşağıdaki bağımlılığı ekleyin:
Groovy
dependencies { def nav_version = "2.10.2" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.10.2" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
Grafik oluşturma
Sunflower uygulamasını temel alan basit bir örneği aşağıda bulabilirsiniz. Bu örnekte iki hedefimiz var: home ve plant_detail. home hedefi, kullanıcı uygulamayı ilk kez başlattığında gösterilir. 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, plant_detail hedefinin gerektirdiği bağımsız değişkenler ve uygulamanın home'den plant_detail'e gitmek için kullandığı to_plant_detail işlemi gösterilmektedir.
home ve plant_detail) ve bunları birbirine bağlayan bir işlem vardır.Kotlin DSL gezinme grafiği barındırma
Uygulamanızın gezinme grafiğini oluşturabilmeniz için grafiği barındıracak bir yere ihtiyacınız vardır. Bu örnekte parçalar kullanıldığından grafik, bir FragmentContainerView içindeki NavHostFragment öğesinde barındırılır:
<!-- 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, res/navigation klasöründe kaynak olarak tanımlanmadığından etkinlikteki onCreate() işleminin bir parçası olarak ayarlanması gerekir.
XML'de bir işlem, hedef kimliğini bir veya daha fazla bağımsız değişkenle birbirine bağlar. Ancak gezinme DSL'si kullanılırken bir rota, rotanın bir parçası olarak bağımsız değişkenler içerebilir. Bu, DSL kullanılırken işlem kavramının olmadığı anlamına gelir.
Bir sonraki adım, grafiğinizi tanımlarken kullanacağınız rotaları tanımlamaktır.
Grafiğiniz için rotalar oluşturma
XML tabanlı gezinme grafikleri, Android derleme sürecinin bir parçası olarak ayrıştırılır. Grafikte tanımlanan her id özelliği için sayısal bir sabit oluşturulur. Derleme zamanında oluşturulan bu statik kimlikler, gezinme grafiğinizi çalışma zamanında oluştururken kullanılamaz. Bu nedenle, Gezinme DSL'si kimlikler yerine serileştirilebilir türleri kullanır. Her rota benzersiz bir türle temsil edilir.
Bağımsız değişkenlerle çalışırken bunlar rota türüne yerleştirilir. Bu sayede gezinme bağımsız değişkenleriniz için tip güvenliği elde edersiniz.
@Serializable data object Home
@Serializable data class Plant(val id: String)
NavGraphBuilder DSL ile grafik oluşturma
Rotaları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, fragment() DSL oluşturucu işlevi kullanılarak iki parça hedefi tanımlanmıştır. Bu işlev için iki tür bağımsız değişkeni gerekir.
İlk olarak, bu hedefin kullanıcı arayüzünü sağlayan bir Fragment sınıfı. Bu ayar, XML kullanılarak tanımlanan parça hedeflerinde android:name özelliğini ayarlamakla aynı etkiye sahiptir.
İkincisi, rota. Bu, Any sınıfından genişletilen, serileştirilebilir bir tür olmalıdır. Bu hedef tarafından kullanılacak tüm gezinme bağımsız değişkenlerini ve bunların türlerini içermelidir.
İşlev, hedef etiket gibi ek yapılandırmalar için isteğe bağlı bir lambda'nın yanı sıra özel bağımsız değişkenler ve derin bağlantılar için yerleşik oluşturucu işlevlerini de kabul eder.
Kotlin DSL grafiğinizle gezinme
Son olarak, NavController.navigate() çağrılarını kullanarak home sayfasından plant_detail sayfasına gidebilirsiniz:
private fun navigateToPlant(plantId: String) {
findNavController().navigate(route = PlantDetail(id = plantId))
}
PlantDetailFragment dilinde, geçerli NavBackStackEntry nesnesini alıp bu nesne üzerinde toRoute yöntemini çağırarak rota örneğini elde edebilir ve navigasyon bağımsız değişkenlerini alabilirsiniz.
val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
PlantDetailFragment, ViewModel kullanıyorsa SavedStateHandle.toRoute kullanarak rota örneğini alın.
val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
Bu kılavuzun geri kalanında, yaygın gezinme grafiği öğeleri, hedefler ve grafiğinizi oluştururken bunları nasıl kullanacağınız açıklanmaktadır.
Hedefler
Kotlin DSL, üç hedef türü için yerleşik destek sağlar:
Fragment, Activity ve NavGraph hedefleri. Bunların her birinin, hedefi oluşturmak ve yapılandırmak için kullanılabilen kendi satır içi uzantı işlevi vardır.
Parça hedefleri
fragment() DSL işlevi, kullanıcı arayüzü için fragment sınıfı ve bu hedefi benzersiz şekilde tanımlamak için kullanılan rota türü ile parametreleştirilebilir. Ardından, Kotlin DSL grafiğinizle gezinme bölümünde açıklandığı gibi ek yapılandırma sağlayabileceğiniz bir lambda gelir.
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 uygulama etkinliği sınıfına göre parametreleştirilmez. Bunun yerine, sondaki lambda'da isteğe bağlı bir activityClass ayarlarsınız. Bu esneklik, açık bir etkinlik sınıfının mantıklı olmayacağı, dolaylı intent kullanılarak başlatılması gereken bir etkinlik için etkinlik hedefi tanımlamanıza olanak tanır. Parça hedeflerinde olduğu gibi etiket, özel bağımsız değişkenler ve derin bağlantılar da yapılandırabilirsiniz.
activity<MyRoute> {
label = getString(R.string.activity_title)
// custom argument types, deepLinks...
activityClass = MyActivity::class
}
Gezinme grafiği hedefi
navigation() DSL işlevi, iç içe yerleştirilmiş gezinme grafiği oluşturmak için kullanılabilir. Bu işlev, bu grafiğe atanacak rota için bir tür parametresi alır. Ayrıca iki bağımsız değişken alır: grafiğin başlangıç hedefinin rotası ve grafiği daha ayrıntılı yapılandırmak için bir lambda. Geçerli öğeler arasında diğer hedefler, özel bağımsız değişken türleri, derin bağlantılar ve hedefin açıklayıcı etiketi yer alır.
Bu etiket, NavigationUI kullanarak gezinme grafiğini kullanıcı arayüzü bileşenlerine bağlamak 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 yeni bir 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 tekli 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ında olduğu gibi tanımlanabilir. Zorunlu bağımsız değişkenler, null değer alabilen türler olarak tanımlanır ve isteğe bağlı bağımsız değişkenler varsayılan değerlerle tanımlanır.
Rotaları ve bağımsız değişkenlerini temsil eden temel mekanizma dize tabanlıdır. Rotaları modellemek için dize kullanmak, yapılandırma değişiklikleri ve sistem tarafından başlatılan işlem sonlandırma sırasında gezinme durumunun depolanmasına ve diskten geri yüklenmesine olanak tanır. Bu nedenle, her gezinme bağımsız değişkeninin serileştirilebilir olması gerekir. Yani bağımsız değişken değerinin bellek içi temsilini String değerine dönüştüren bir yöntemi olmalıdır.
Kotlin serileştirme eklentisi, bir nesneye @Serializable ek açıklaması eklendiğinde temel türler için otomatik olarak serileştirme yöntemleri oluşturur.
@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 sayede, türünüzün bir rotadan veya derin bağlantıdan nasıl ayrıştırılacağını tam olarak kontrol edebilirsiniz.
Ö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 alınabilir:
val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters
Derin bağlantılar
Derin bağlantılar, XML tabanlı bir gezinme grafiğinde olduğu gibi herhangi bir hedefe eklenebilir. Hedef için derin bağlantı oluşturma bölümünde tanımlanan tüm prosedürler, Kotlin DSL kullanarak derin bağlantı oluşturma süreci için de geçerlidir.
Ancak kapalı derin bağlantı oluştururken <deepLink> öğeleri için analiz edilebilecek bir XML gezinme kaynağınız yoktur. Bu nedenle, AndroidManifest.xml dosyanıza <nav-graph> öğesi yerleştiremezsiniz. Bunun yerine, etkinliğinize manuel olarak intent filtreleri eklemeniz gerekir. Sağladığınız intent filtresi, uygulamanızın derin bağlantılarının temel yolu, işlemi ve MIME türüyle eşleşmelidir.
Derin bağlantılar, hedefin lambda'sındaki deepLink işlevi çağrılarak hedefe eklenir. Rotayı parametreleştirilmiş bir tür olarak ve derin bağlantı için kullanılan URL'nin temel yolu için bir basePath parametresini kabul eder.
deepLinkBuilder sondaki lambda'yı kullanarak bir işlem ve MIME türü de ekleyebilirsiniz.
Aşağıdaki örnekte, Home hedefi için bir derin bağlantı URI'si oluşturulmaktadı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 eklenir (örnek:
/{id}) - Varsayılan değere sahip parametreler (isteğe bağlı parametreler) sorgu parametreleri olarak eklenir (örnek:
?name={name}) - Koleksiyonlar, sorgu parametreleri olarak eklenir (örnek:
?items={value1}&items={value2}) - Parametrelerin sırası, rotadaki alanların sırası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() yöntemini her çağırdığınızda, ilgili hedef için tutulan listeye yeni bir derin bağlantı eklenir.
Sınırlamalar
Safe Args eklentisi, Directions ve Arguments sınıflarını oluşturmak için XML kaynak dosyalarını aradığından Kotlin DSL ile uyumlu değildir.