Compose Navigation
Compose Navigation
迁移到Navigation
添加依赖项
dependencies {
implementation "androidx.navigation:navigation-compose:{latest_version}"
// ...
}
https://developer.android.com/jetpack/androidx/releases/navigation?hl=zh-cn
在这个链接处找到最新版 Navigation Compose。
设置NavController
我们使用NavController来操纵堆栈,即控制页面的路由,我们需要在Screen处就创建NavController,然后,所有需要引用 NavController
的可组合项都可以访问它
设置NavHost
Navigation 的 3 个主要部分是 NavController
、NavGraph
和 NavHost
。NavController
始终与一个 NavHost
可组合项相关联。NavHost
充当容器,负责显示导航图的当前所在页面。当在可组合项之间进行导航时,NavHost
的内容会自动进行重组。
设置NavGraph
使用 [NavGraphBuilder.composable
](https://developer.android.com/reference/kotlin/androidx/navigation/compose/package-summary?hl=zh-cn#(androidx.navigation.NavGraphBuilder).composable(kotlin.String, kotlin.collections.List, kotlin.collections.List, kotlin.Function1)) 扩展函数来将各个可组合目的地添加到导航图中,并定义必要的导航信息。接收的参数为一个string类型的路由地址,还可以配置带参数的导航以及deepLink
扩展函数里的则是对应的screen
此时我们可以写出这样的代码
@Composable
fun RallyNavHost(
navController: NavHostController,
modifier: Modifier = Modifier
) {
NavHost(
navController = navController,
startDestination = Overview.route,
modifier = modifier
) {
composable(route = Overview.route) {
OverviewScreen(
onClickSeeAllAccounts = {
navController.navigateSingleTopTo(Accounts.route)
},
onClickSeeAllBills = {
navController.navigateSingleTopTo(Bills.route)
},
onAccountClick = { accountType ->
navController.navigateToSingleAccount(accountType)
}
)
}
composable(route = Accounts.route) {
AccountsScreen(
onAccountClick = { accountType ->
navController.navigateToSingleAccount(accountType)
}
)
}
composable(route = Bills.route) {
BillsScreen()
}
composable(
route = SingleAccount.routeWithArgs,
arguments = SingleAccount.arguments,
deepLinks = SingleAccount.deepLinks
) { navBackStackEntry ->
val accountType =
navBackStackEntry.arguments?.getString(SingleAccount.accountTypeArg)
SingleAccountScreen(accountType)
}
}
}
其中RallyNavHost是对NavHost的封装
每一个composable代表一个可路由项
导航堆栈控制
防止重复导航
比如我们重复调用navigate到同一个页面,这显然是不行的,应该是只创建单一页面,类似于创建Activity里的SingleTop
可以使用navController.navigate(route) { launchSingleTop = true }
解决
回到最开始的页面
可以使用
popUpTo(startDestination) { saveState = true }` - 弹出到导航图的起始目的地,以免在您选择标签页时在返回堆栈上积累大量目的地
restoreState = true- 确定此导航操作是否应恢复
PopUpToBuilder.saveState或
popUpToSaveState` 属性之前保存的任何状态。请注意,如果之前未使用要导航到的目的地 ID 保存任何状态,此项不会产生任何影响
我们可以封装上述操作为一个扩展函数:
fun NavHostController.navigateSingleTopTo(route: String) =
this.navigate(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(
this@navigateSingleTopTo.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
}
tab状态更改
我们需要在重组过程中得到当前最新的页面从而更改tab的选中状态,流程大概是这样的
tab提供选中的click监听—某个tab被click—使用navigator改变页面—触发重组—重组过程中拿到最新的页面—通过最新的页面去更改tab的状态
可以通过下面俩行代码拿到最新的页面
val currentBackStack by navController.currentBackStackEntryAsState()
val currentDestination = currentBackStack?.destination
然后就可以通过currentDestination去更新tab了
带参数的Navigate以及Deep Link
带参数的导航
带参数的Navigate以及Deep Link需要在composable扩展函数中定义arguments参数,比如
composable(
route =
"${SingleAccount.route}/{${SingleAccount.accountTypeArg}}",
arguments = listOf(
navArgument(SingleAccount.accountTypeArg) { type = NavType.StringType }
)
) {
SingleAccountScreen()
}
object SingleAccount : RallyDestination {
// Added for simplicity, this icon will not in fact be used, as SingleAccount isn't
// part of the RallyTabRow selection
override val icon = Icons.Filled.Money
override val route = "single_account"
const val accountTypeArg = "account_type"
val routeWithArgs = "$route/{$accountTypeArg}"
val arguments = listOf(
navArgument(accountTypeArg) { type = NavType.StringType }
)
val deepLinks = listOf(
navDeepLink { uriPattern = "rally://$route/{$accountTypeArg}" }
)
}
然后,就可以使用
navBackStackEntry.arguments?.getString(SingleAccount.accountTypeArg)
获取Navigate过程携带的参数了
Deep Link
首先要配置AndroidManifest
通过 <activity>
内的 <intent-filter>
创建一个新的 intent 过滤器,相应操作为 VIEW
,类别为 BROWSABLE
和 DEFAULT
。使用 data
标记添加 **scheme
**和host
<activity
android:name=".RallyActivity"
android:windowSoftInputMode="adjustResize"
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="rally" android:host="single_account" />
</intent-filter>
</activity>
接下来在composable扩展函数中配置deeplink
composable(
route = SingleAccount.routeWithArgs,
// ...
deepLinks = listOf(navDeepLink {
uriPattern = "rally://${SingleAccount.route}/{${SingleAccount.accountTypeArg}}"
})
)
使用adb验证:
在已连接的模拟器或设备上重新安装应用,打开命令行并执行以下命令,以便模拟深层链接启动
adb shell am start -d "rally://single_account/Checking" -a android.intent.action.VIEW
标题:Compose Navigation
作者:OkAndGreat
地址:http://zhongtai521.wang/articles/2023/05/26/1691545336695.html