Gezinme bileşeni, Jetpack Compose uygulamaları için destek sağlar. Gezinme bileşeninin altyapısından ve özelliklerinden yararlanırken composable'lar arasında gezinebilirsiniz.
Kurulum
Compose'u desteklemek için uygulama modülünüzün build.gradle
dosyasında aşağıdaki bağımlılığı kullanın:
Groovy
dependencies { def nav_version = "2.7.7" implementation "androidx.navigation:navigation-compose:$nav_version" }
Kotlin
dependencies { val nav_version = "2.7.7" implementation("androidx.navigation:navigation-compose:$nav_version") }
Başlayın
Bir uygulamada gezinmeyi uygularken gezinme ana makinesi, grafik ve denetleyici uygulayın. Daha fazla bilgi için Gezinmeye genel bakış bölümünü inceleyin.
NavController oluşturma
Compose'da NavController
oluşturma hakkında bilgi edinmek için Gezinme denetleyicisi oluşturma başlıklı makalenin Oluşturma bölümüne bakın.
NavHost oluşturma
Compose'da NavHost
oluşturma hakkında bilgi için Gezinme grafiğinizi tasarlama başlıklı makalenin Oluşturma bölümüne bakın.
Bir composable'a git
Bir Composable'a gitme hakkında bilgi için mimari belgelerindeki Bir hedefe gitme bölümüne bakın.
Bağımsız değişkenlerle gezin
Gezinme Oluşturma, composable hedefler arasında bağımsız değişkenlerin iletilmesini de destekler. Bunu yapmak için, temel gezinme kitaplığını kullanırken derin bağlantıya bağımsız değişkenler ekleme işlemine benzer şekilde, yönlendirmenize bağımsız değişken yer tutucuları eklemeniz gerekir:
NavHost(startDestination = "profile/{userId}") {
...
composable("profile/{userId}") {...}
}
Varsayılan olarak, tüm bağımsız değişkenler dize olarak ayrıştırılır. composable()
öğesinin arguments
parametresi, NamedNavArgument
nesnelerinin listesini kabul eder. navArgument()
yöntemini kullanarak hızlı bir şekilde NamedNavArgument
oluşturabilir ve ardından tam type
değerini belirtebilirsiniz:
NavHost(startDestination = "profile/{userId}") {
...
composable(
"profile/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType })
) {...}
}
composable()
işlevinin lambdasında bulunan NavBackStackEntry
bağımsız değişkenlerini ayıklamanız gerekir.
composable("profile/{userId}") { backStackEntry ->
Profile(navController, backStackEntry.arguments?.getString("userId"))
}
Bağımsız değişkeni hedefe iletmek için navigate
çağrısı yaparken URL'yi rotaya eklemeniz gerekir:
navController.navigate("profile/user1234")
Desteklenen türlerin listesi için Hedefler arasında veri aktarma bölümünü inceleyin.
Navigasyon sırasında karmaşık verileri alma
Gezinirken karmaşık veri nesnelerini aktarmamanız, bunun yerine gezinme işlemlerini gerçekleştirirken bağımsız tanımlayıcı veya başka bir kimlik biçimi gibi gerekli minimum bilgileri iletmeniz önemle tavsiye edilir:
// Pass only the user ID when navigating to a new destination as argument
navController.navigate("profile/user1234")
Karmaşık nesneler, veri katmanı gibi tek bir doğruluk kaynağında veri olarak depolanmalıdır. Rotayı izledikten sonra hedefinize ulaştığınızda, iletilen kimliği kullanarak gerekli bilgileri tek doğru kaynaktan yükleyebilirsiniz. ViewModel
içinde veri katmanına erişmekten sorumlu bağımsız değişkenleri almak için ViewModel
öğesinin SavedStateHandle
öğesini kullanın:
class UserViewModel(
savedStateHandle: SavedStateHandle,
private val userInfoRepository: UserInfoRepository
) : ViewModel() {
private val userId: String = checkNotNull(savedStateHandle["userId"])
// Fetch the relevant user information from the data layer,
// ie. userInfoRepository, based on the passed userId argument
private val userInfo: Flow<UserInfo> = userInfoRepository.getUserInfo(userId)
// …
}
Bu yaklaşım, yapılandırma değişiklikleri sırasında veri kaybının ve söz konusu nesne güncellenirken ya da dönüştürüldüğünde yaşanabilecek tutarsızlıkların önlenmesine yardımcı olur.
Karmaşık verileri bağımsız değişken olarak iletmekten neden kaçınmanız gerektiği hakkında daha ayrıntılı bir açıklama ve desteklenen bağımsız değişken türlerinin listesi için Verileri hedefler arasında iletme bölümünü inceleyin.
İsteğe bağlı bağımsız değişkenler ekleyin
Gezinme Oluşturma, isteğe bağlı gezinme bağımsız değişkenlerini de destekler. İsteğe bağlı bağımsız değişkenler, gerekli bağımsız değişkenlerden iki şekilde ayrılır:
- Sorgu parametresi söz dizimi (
"?argName={argName}"
) kullanılarak eklenmelidir - Bunların bir
defaultValue
öğesi ayarlanmış veyanullable = true
(varsayılan değeri dolaylı olaraknull
değerine ayarlar) içermesi gerekir.
Bu, isteğe bağlı tüm bağımsız değişkenlerin, composable()
işlevine liste olarak açıkça eklenmesi gerektiği anlamına gelir:
composable(
"profile?userId={userId}",
arguments = listOf(navArgument("userId") { defaultValue = "user1234" })
) { backStackEntry ->
Profile(navController, backStackEntry.arguments?.getString("userId"))
}
Artık hedefe hiçbir bağımsız değişken aktarılmasa bile bunun yerine defaultValue
("user1234") kullanılır.
Rotalar aracılığıyla bağımsız değişkenleri ele alma yapısı, composable'larınızın Navigasyon'dan tamamen bağımsız olduğu ve çok daha test edilebilir hale geldiği anlamına gelir.
Derin bağlantılar
Gezinme Oluşturma, composable()
işlevinin bir parçası olarak tanımlanabilecek örtülü derin bağlantıları da destekler. deepLinks
parametresi, navDeepLink()
yöntemiyle hızlıca oluşturulabilecek NavDeepLink
nesnelerinin listesini kabul eder:
val uri = "https://www.example.com"
composable(
"profile?id={id}",
deepLinks = listOf(navDeepLink { uriPattern = "$uri/{id}" })
) { backStackEntry ->
Profile(navController, backStackEntry.arguments?.getString("id"))
}
Bu derin bağlantılar, belirli bir URL'yi, işlemi veya MIME türünü bir composable ile ilişkilendirmenize olanak tanır. Varsayılan olarak, bu derin bağlantılar harici uygulamalara gösterilmez. Bu derin bağlantıları harici olarak kullanılabilir hale getirmek için uygulamanızın manifest.xml
dosyasına uygun <intent-filter>
öğelerini eklemeniz gerekir. Önceki örnekte derin bağlantıyı etkinleştirmek için manifest'in <activity>
öğesinin içine aşağıdakileri eklemeniz gerekir:
<activity …>
<intent-filter>
...
<data android:scheme="https" android:host="www.example.com" />
</intent-filter>
</activity>
Gezinme, derin bağlantı başka bir uygulama tarafından tetiklendiğinde otomatik olarak söz konusu composable'a yönlendiren derin bağlantılardır.
Aynı derin bağlantılar, bir composable'dan uygun derin bağlantıyla PendingIntent
oluşturmak için de kullanılabilir:
val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
Intent.ACTION_VIEW,
"https://www.example.com/$id".toUri(),
context,
MyActivity::class.java
)
val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
addNextIntentWithParentStack(deepLinkIntent)
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}
Daha sonra bu deepLinkPendingIntent
, uygulamanızı derin bağlantı hedefinde açmak için diğer PendingIntent
gibi kullanabilirsiniz.
İç İçe Navigasyon
İç içe gezinme grafikleri oluşturma hakkında bilgi edinmek için İç içe yerleştirilmiş grafikler bölümüne bakın.
Alttaki gezinme çubuğuyla entegrasyon
NavController
öğesini composable hiyerarşinizde daha üst bir düzeyde tanımlayarak Navigasyon'u alt gezinme bileşeni gibi diğer bileşenlere bağlayabilirsiniz. Bunu yaptığınızda, alt çubuktaki simgeleri seçerek gezinebilirsiniz.
BottomNavigation
ve BottomNavigationItem
bileşenlerini kullanmak için Android uygulamanıza androidx.compose.material
bağımlılığını ekleyin.
Modern
dependencies { implementation "androidx.compose.material:material:1.6.8" } android { buildFeatures { compose true } composeOptions { kotlinCompilerExtensionVersion = "1.5.15" } kotlinOptions { jvmTarget = "1.8" } }
Kotlin
dependencies { implementation("androidx.compose.material:material:1.6.8") } android { buildFeatures { compose = true } composeOptions { kotlinCompilerExtensionVersion = "1.5.15" } kotlinOptions { jvmTarget = "1.8" } }
Alt gezinme çubuğundaki öğeleri gezinme grafiğinizdeki rotalara bağlamak için burada görünen Screen
gibi, hedeflerin rotasını ve dize kaynak kimliğini içeren mühürlü bir sınıf tanımlamanız önerilir.
sealed class Screen(val route: String, @StringRes val resourceId: Int) {
object Profile : Screen("profile", R.string.profile)
object FriendsList : Screen("friendslist", R.string.friends_list)
}
Ardından bu öğeleri, BottomNavigationItem
tarafından kullanılabilecek bir listeye yerleştirin:
val items = listOf(
Screen.Profile,
Screen.FriendsList,
)
BottomNavigation
composable'ınızda currentBackStackEntryAsState()
işlevini kullanarak geçerli NavBackStackEntry
değerini alın. Bu giriş, mevcut NavDestination
öğesine erişmenizi sağlar. Daha sonra her bir BottomNavigationItem
öğesinin seçili durumu, öğenin rotası ile mevcut hedefin ve üst hedeflerinin rotası karşılaştırılarak, NavDestination
hiyerarşisini kullanarak iç içe yerleştirilmiş gezinme kullandığınız durumların ele alınmasıyla belirlenebilir.
Öğenin rotası, onClick
lambda'yı navigate
çağrısına bağlamak için de kullanılır. Böylece öğeye dokunduğunuzda ilgili öğeye yönlendirilirsiniz. saveState
ve restoreState
işaretleri kullanıldığında, bu öğenin durumu ve geri yığını doğru şekilde kaydedilir ve siz alttaki gezinme öğeleri arasında geçiş yaparken geri yüklenir.
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
items.forEach { screen ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(navController, startDestination = Screen.Profile.route, Modifier.padding(innerPadding)) {
composable(Screen.Profile.route) { Profile(navController) }
composable(Screen.FriendsList.route) { FriendsList(navController) }
}
}
Burada navController
durumunu NavHost
işlevinden kaldırmak ve BottomNavigation
bileşeniyle paylaşmak için NavController.currentBackStackEntryAsState()
yönteminden yararlanacaksınız. Bu, BottomNavigation
öğesinin otomatik olarak en güncel duruma sahip olduğu anlamına gelir.
Yazmada Gezinme'de güvenlik yazın
Bu sayfadaki kod tür için güvenli değil. navigate()
işlevini var olmayan rotalar veya yanlış bağımsız değişkenlerle çağırabilirsiniz. Bununla birlikte, gezinme kodunuzu çalışma zamanında tür açısından güvenli olacak şekilde yapılandırabilirsiniz. Bu sayede kilitlenmeleri önleyebilir ve şunlardan emin olabilirsiniz:
- Bir hedef veya gezinme grafiğine giderken sağladığınız bağımsız değişkenler doğru türdedir ve gerekli tüm bağımsız değişkenlerin mevcut olması gerekir.
SavedStateHandle
öğesinden aldığınız bağımsız değişkenler doğru türdedir.
Bu hakkında daha fazla bilgi için Kotlin DSL ve Gezinme Oluşturma Aracı'nda tür güvenliği bölümüne bakın.
Birlikte çalışabilirlik
Oluştur ile Gezinme bileşenini kullanmak istiyorsanız iki seçeneğiniz vardır:
- Parçalar için Gezinme bileşeniyle bir gezinme grafiği tanımlayın.
- Oluştur'da Oluştur hedeflerini kullanarak
NavHost
ile bir gezinme grafiği tanımlayın. Bu yalnızca gezinme grafiğindeki tüm ekranlar composable ise mümkündür.
Bu nedenle, karma Oluşturma ve Görünümler uygulamaları için önerimiz Parça tabanlı Gezinme bileşenini kullanmaktır. Parçalar; Görüntüleme tabanlı ekranları, Oluşturma ekranlarını ve hem Görünümler hem de Oluştur'u kullanan ekranları tutar. Her bir Parça'nın içeriği Oluşturma'ya geçtikten sonraki adım, tüm bu ekranları Gezinme Oluşturma özelliği ile birbirine bağlamak ve tüm Parçaları kaldırmaktır.
Parçalar için Gezinme ile Oluştur'dan gitme
Compose kodu içindeki hedefleri değiştirmek için hiyerarşideki herhangi bir composable'a iletilebilen ve tetiklenebilen etkinlikleri kullanıma sunarsınız.
@Composable
fun MyScreen(onNavigate: (Int) -> Unit) {
Button(onClick = { onNavigate(R.id.nav_profile) } { /* ... */ }
}
Parçanızda, NavController
öğesini bulup hedefe giderek Oluşturma ile parça tabanlı Gezinme bileşeni arasında bir köprü oluşturursunuz:
override fun onCreateView( /* ... */ ) {
setContent {
MyScreen(onNavigate = { dest -> findNavController().navigate(dest) })
}
}
Alternatif olarak NavController
öğesini Compose hiyerarşinizde aşağı aktarabilirsiniz.
Ancak basit işlevlerin gösterilmesi çok daha fazla yeniden kullanılabilir ve test edilebilir.
Test etme
Gezinme kodunu composable hedeflerinizden ayırarak her composable'ı NavHost
composable'dan ayrı olarak test edebilirsiniz.
Bu, navController
öğesini doğrudan herhangi bir composable öğesine iletmemeniz ve bunun yerine gezinme geri çağırmalarını parametre olarak iletmeniz gerektiği anlamına gelir. Bu, testlerde navController
örneği gerektirmediğinden tüm composable'larınızın ayrı ayrı test edilebilir.
composable
lambda'sının sağladığı dolaylı yönlendirme düzeyi, Gezinme kodunuzu
composable'dan ayırmanızı sağlar. Bu yöntem iki yönde çalışır:
- composable'ınıza yalnızca ayrıştırılan bağımsız değişkenleri iletin
- Gezinmek için
NavController
yerine composable tarafından tetiklenmesi gereken lambda'ları iletin.
Örneğin, giriş olarak userId
alan ve kullanıcıların bir arkadaşının profil sayfasına gitmesine izin veren Profile
composable'ı şu imzaya sahip olabilir:
@Composable
fun Profile(
userId: String,
navigateToFriendProfile: (friendUserId: String) -> Unit
) {
…
}
Bu şekilde, Profile
composable, Navigasyon'dan bağımsız çalışarak bağımsız olarak test edilebilir. composable
lambda'sı, Gezinme API'leri ile composable'ınız arasındaki boşluğu kapatmak için gereken minimum mantığı içerir.
composable(
"profile?userId={userId}",
arguments = listOf(navArgument("userId") { defaultValue = "user1234" })
) { backStackEntry ->
Profile(backStackEntry.arguments?.getString("userId")) { friendUserId ->
navController.navigate("profile?userId=$friendUserId")
}
}
NavHost
, composable'larınıza iletilen gezinme işlemleri ve bağımsız ekran composable'larınızı test ederek uygulamada gezinmeyle ilgili ihtiyaçlarınızı karşılayacak testler yazmanız önerilir.
NavHost
test ediliyor
NavHost
öğenizi test etmeye başlamak için aşağıdaki gezinme testi bağımlılığını ekleyin:
dependencies {
// ...
androidTestImplementation "androidx.navigation:navigation-testing:$navigationVersion"
// ...
}
NavHost
test konunuzu oluşturabilir ve navController
örneğinin bir örneğini aktarabilirsiniz. Gezinme testi yapısı bunun için bir TestNavHostController
sağlar. Uygulamanızın ve NavHost
'nin başlangıç hedefini doğrulayan kullanıcı arayüzü testi aşağıdaki gibi görünür:
class NavigationTest {
@get:Rule
val composeTestRule = createComposeRule()
lateinit var navController: TestNavHostController
@Before
fun setupAppNavHost() {
composeTestRule.setContent {
navController = TestNavHostController(LocalContext.current)
navController.navigatorProvider.addNavigator(ComposeNavigator())
AppNavHost(navController = navController)
}
}
// Unit test
@Test
fun appNavHost_verifyStartDestination() {
composeTestRule
.onNodeWithContentDescription("Start Screen")
.assertIsDisplayed()
}
}
Gezinme işlemlerini test etme
Kullanıcı arayüzü öğelerine tıklayarak ve ardından görüntülenen hedefi doğrulayarak veya beklenen rotayı mevcut rotayla karşılaştırarak navigasyon uygulamanızı çeşitli şekillerde test edebilirsiniz.
Beton uygulamanızın uygulamasını test etmek istediğiniz için kullanıcı arayüzüne tıklama tercih edilir. Bunu bağımsız composable işlevleriyle birlikte ayrı ayrı test etmeyi öğrenmek için Jetpack Compose'da test etme codelab'ine göz atmayı unutmayın.
Ayrıca, navController
currentBackStackEntry
ile mevcut dize rotasını beklenen rotayla karşılaştırarak onaylarınızı kontrol etmek için navController
kullanabilirsiniz:
@Test
fun appNavHost_clickAllProfiles_navigateToProfiles() {
composeTestRule.onNodeWithContentDescription("All Profiles")
.performScrollTo()
.performClick()
val route = navController.currentBackStackEntry?.destination?.route
assertEquals(route, "profiles")
}
Compose testinin temelleri hakkında daha fazla yardım için Compose düzeninizi test etme ve Jetpack Compose'da test etme codelab'ine göz atın. Gezinme kodunu ileri düzey test etme hakkında daha fazla bilgi edinmek için Gezinme Testi kılavuzunu ziyaret edin.
Daha fazla bilgi
Jetpack Navigasyon hakkında daha fazla bilgi edinmek için Gezinme bileşenini kullanmaya başlama bölümüne bakın veya Jetpack Compose Navigasyon codelab'e katılın.
Uygulamanızın gezinme şeklini farklı ekran boyutlarına, yönlere ve form faktörlerine uyum sağlayacak şekilde nasıl tasarlayacağınızı öğrenmek için Duyarlı kullanıcı arayüzleri için gezinme bölümüne bakın.
Modüler hale getirilmiş bir uygulamada, iç içe yerleştirilmiş grafikler ve alt gezinme çubuğu entegrasyonu gibi kavramlar dahil olmak üzere daha gelişmiş bir Compose gezinme uygulaması hakkında bilgi edinmek için GitHub'daki Now in Android uygulamasına göz atın.
Numuneler
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir
- Compose'da Materyal Tasarım 2
- Jetpack Gezinme'yi Gezinme Oluşturma'ya taşıma
- Kaldırılacak bölge durumu