Navigation 导航
问题
Jetpack Navigation 组件如何管理页面导航?
答案
核心组件
| 组件 | 说明 |
|---|---|
| NavHost | 容器,显示当前目的地 |
| NavController | 控制器,管理导航操作 |
| NavGraph | 导航图,定义所有目的地和路由 |
Fragment Navigation
<!-- res/navigation/nav_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/nav_graph"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.example.HomeFragment"
android:label="Home">
<action
android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/detailFragment"
android:name="com.example.DetailFragment"
android:label="Detail">
<argument
android:name="itemId"
app:argType="long" />
</fragment>
</navigation>
// Safe Args 类型安全导航
// 导航到详情页
val action = HomeFragmentDirections.actionHomeToDetail(itemId = 42L)
findNavController().navigate(action)
// 接收参数
class DetailFragment : Fragment() {
private val args: DetailFragmentArgs by navArgs()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val itemId = args.itemId
}
}
Compose Navigation
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") {
HomeScreen(
onItemClick = { id -> navController.navigate("detail/$id") }
)
}
composable(
route = "detail/{itemId}",
arguments = listOf(navArgument("itemId") { type = NavType.LongType })
) { backStackEntry ->
val itemId = backStackEntry.arguments?.getLong("itemId") ?: 0
DetailScreen(itemId)
}
}
}
底部导航集成
@Composable
fun MainScreen() {
val navController = rememberNavController()
Scaffold(
bottomBar = {
NavigationBar {
val currentRoute = navController.currentBackStackEntryAsState()
.value?.destination?.route
bottomNavItems.forEach { item ->
NavigationBarItem(
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.startDestinationId) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = { Icon(item.icon, null) },
label = { Text(item.label) }
)
}
}
}
) { padding ->
NavHost(navController, startDestination = "home", Modifier.padding(padding)) {
composable("home") { HomeScreen() }
composable("search") { SearchScreen() }
composable("profile") { ProfileScreen() }
}
}
}
常见面试问题
Q1: popUpTo 和 launchSingleTop 的作用?
答案:
popUpTo:导航前先从回退栈中弹出到指定目的地,避免栈中累积重复页面launchSingleTop:如果目标已在栈顶,不创建新实例(类似 Activity 的singleTop)
底部导航切换 Tab 时通常组合使用,保证栈中不会有重复的 Tab 页。
Q2: Navigation 如何实现深度链接(Deep Link)?
答案:
composable(
route = "detail/{itemId}",
deepLinks = listOf(navDeepLink { uriPattern = "https://example.com/item/{itemId}" })
) { ... }
当用户通过 URL https://example.com/item/42 打开应用时,直接跳转到详情页。