跳到主要内容

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: popUpTolaunchSingleTop 的作用?

答案

  • popUpTo:导航前先从回退栈中弹出到指定目的地,避免栈中累积重复页面
  • launchSingleTop:如果目标已在栈顶,不创建新实例(类似 Activity 的 singleTop

底部导航切换 Tab 时通常组合使用,保证栈中不会有重复的 Tab 页。

答案

composable(
route = "detail/{itemId}",
deepLinks = listOf(navDeepLink { uriPattern = "https://example.com/item/{itemId}" })
) { ... }

当用户通过 URL https://example.com/item/42 打开应用时,直接跳转到详情页。

相关链接