跳到主要内容

Navigation 组件与页面导航

问题

Jetpack Navigation 组件是如何简化 Android 页面导航的?与传统 Fragment 事务有什么区别?

答案

1. Navigation 组件架构

Navigation 三大核心组件:

组件职责
NavHost显示导航目的地的容器(通常是 NavHostFragment
NavController控制导航操作(前进、返回、弹出栈等)
NavGraph定义所有目的地和导航路径的图

2. 基本使用

<!-- res/navigation/nav_graph.xml -->
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/homeFragment">

<fragment
android:id="@+id/homeFragment"
android:name="com.example.HomeFragment"
android:label="首页">
<action
android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
</fragment>

<fragment
android:id="@+id/detailFragment"
android:name="com.example.DetailFragment"
android:label="详情">
<argument
android:name="itemId"
app:argType="integer" />
</fragment>
</navigation>
<!-- Activity 布局 -->
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true" />

3. Safe Args(类型安全参数传递)

Safe Args 是一个 Gradle 插件,自动生成类型安全的导航代码:

// build.gradle.kts
plugins {
id("androidx.navigation.safeargs.kotlin")
}
// 发送方 - 自动生成 Directions 类
val action = HomeFragmentDirections.actionHomeToDetail(itemId = 42)
findNavController().navigate(action)

// 接收方 - 自动生成 Args 类
class DetailFragment : Fragment() {
private val args: DetailFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val itemId = args.itemId // 类型安全,无需 key 字符串
}
}
<!-- 导航图中声明 Deep Link -->
<fragment android:id="@+id/detailFragment">
<deepLink
android:id="@+id/deepLink"
app:uri="myapp://detail/{itemId}" />
</fragment>
<!-- AndroidManifest.xml -->
<activity android:name=".MainActivity">
<nav-graph android:value="@navigation/nav_graph" />
</activity>
// 代码触发 Deep Link
val request = NavDeepLinkRequest.Builder
.fromUri("myapp://detail/42".toUri())
.build()
findNavController().navigate(request)

5. BottomNavigationView 集成

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val navController = findNavController(R.id.nav_host_fragment)
// 一行代码绑定底部导航
findViewById<BottomNavigationView>(R.id.bottom_nav)
.setupWithNavController(navController)
}
}

6. Navigation vs 传统 Fragment 事务

特性Navigation 组件传统 FragmentTransaction
可视化支持导航图编辑器无可视化工具
参数传递Safe Args 类型安全Bundle 手动管理
Deep Link内置支持需手动实现
回退栈自动管理手动 addToBackStack
动画导航图中声明代码或 XML 配置
底部导航setupWithNavController手动同步状态

常见面试问题

Q1: Navigation 组件如何处理回退栈?

答案

Navigation 自动管理回退栈。每次 navigate() 会将目的地压栈,按返回键自动弹栈。也可通过 popUpTopopUpToInclusive 控制弹栈行为:

<action
android:id="@+id/action_to_home"
app:destination="@id/homeFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />

这会清除 homeFragment 之上(含自身)的所有页面,避免重复创建。

Q2: Navigation 组件的 Fragment 每次切换都会重建视图吗?如何优化?

答案

默认每次导航到一个 Fragment 都会重建视图。优化方案:

  • Navigation 2.4+ 支持多回退栈saveState / restoreState),配合 BottomNavigationView 可保持各 Tab 的回退栈和状态
  • 使用 ViewModel 持有数据,视图重建后自动恢复
  • 使用 SavedStateHandle 保存轻量状态

Q3: Safe Args 和 Bundle 传参有什么区别?

答案

  • Safe Args:编译时类型检查,自动生成 DirectionsArgs 类,IDE 有代码补全。缺点是需要额外 Gradle 插件
  • Bundle:运行时通过 key 取值,容易拼错 key 或类型不匹配导致崩溃
  • Safe Args 底层仍然使用 Bundle,只是封装了类型安全的访问层

相关链接