
引言
Navigation Component 是 Android Jetpack 的一部分,用于简化应用内导航的实现。它提供了统一的导航 API,支持 Fragment、Activity 以及自定义导航目标,同时自动处理返回栈、过渡动画、深层链接和类型安全的参数传递(Safe Args)。传统方式中,Fragment 的添加、替换、返回栈管理需要大量样板代码,且容易出错。Navigation Component 通过声明式导航图和集中化的 NavController,大幅简化了这些逻辑。本文将深入探讨 Navigation Component 的核心用法、Safe Args、返回栈管理以及最佳实践。
为什么使用 Navigation Component
传统 Fragment 管理依赖 FragmentManager 和 FragmentTransaction,手动处理 add、replace、addToBackStack 等,代码冗长且易遗漏返回栈逻辑。Navigation Component 将导航结构声明在 XML 导航图中,通过 NavController 执行导航,自动处理返回栈和过渡动画。Safe Args 插件可生成类型安全的导航参数类,避免手写 Bundle 时的键名错误和类型不匹配。此外,Navigation 与 Toolbar、BottomNavigationView 等 UI 组件集成,可自动同步标题和选中状态。
添加依赖
dependencies {
implementation("androidx.navigation:navigation-fragment-ktx:2.7.6")
implementation("androidx.navigation:navigation-ui-ktx:2.7.6")
}
若使用 Safe Args,需在项目级 build.gradle 添加 navigation-safe-args-gradle-plugin,并在应用级应用该插件。
创建导航图
导航图是 XML 文件,定义所有导航目标和它们之间的 action。startDestination 指定默认显示的目标。每个 fragment 可定义多个 action,指向不同的 destination。argument 声明目标所需的参数及类型,Safe Args 会根据此生成 *Args 和 *Directions 类。enterAnim、exitAnim 等可配置过渡动画。
<!-- 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="Home">
<action
android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left" />
</fragment>
<fragment
android:id="@+id/detailFragment"
android:name="com.example.DetailFragment"
android:label="Detail">
<argument
android:name="itemId"
app:argType="string" />
</fragment>
</navigation>
在 Activity 中配置
NavHostFragment 是导航目标的容器,通过 navGraph 指定导航图。defaultNavHost="true" 表示该 NavHost 会拦截系统返回键,用于弹出返回栈而非退出 Activity。Activity 的布局中只需一个 FragmentContainerView,无需为每个 Fragment 单独声明。
<androidx.constraintlayout.widget.ConstraintLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
执行导航
使用 Safe Args 时,通过生成的 *Directions 类创建 action,参数类型安全。也可使用 navigate(resId, bundle) 直接传递 Bundle,但需确保键名和类型与目标声明的 argument 一致。findNavController() 可在 Fragment 或 View 中获取 NavController,需确保调用时 Fragment 已添加到 NavHost。
// 使用 Safe Args 传递参数
val action = HomeFragmentDirections.actionHomeToDetail(itemId = "123")
findNavController().navigate(action)
// 使用 Bundle
findNavController().navigate(
R.id.detailFragment,
bundleOf("itemId" to "123")
)
接收参数
使用 Safe Args 时,通过 by navArgs() 委托获取参数,类型安全且无需手动解析 Bundle。navArgs() 会从 arguments 中读取并构造 DetailFragmentArgs 实例。
class DetailFragment : Fragment() {
private val args: DetailFragmentArgs by navArgs()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val itemId = args.itemId
loadDetail(itemId)
}
}
返回栈与 Pop 行为
popBackStack() 弹出栈顶;popBackStack(R.id.homeFragment, false) 弹出直到指定目标(inclusive 参数控制是否包含该目标)。NavOptions 可配置 popUpTo、动画等,实现”跳转到详情并清空中间页”等复杂导航逻辑。例如登录成功后 popUpTo 登录页并 inclusive,可清除登录页及之上的所有页面。
// 弹出到指定目标
findNavController().popBackStack(R.id.homeFragment, false)
// 使用 NavOptions 配置
val navOptions = NavOptions.Builder()
.setPopUpTo(R.id.homeFragment, true)
.setEnterAnim(R.anim.slide_in_right)
.build()
findNavController().navigate(R.id.detailFragment, null, navOptions)
总结
Navigation Component 提供了声明式的导航方式,简化了 Fragment 管理和页面跳转逻辑。掌握导航图、Safe Args、返回栈和 NavOptions,可以构建清晰、可维护的导航结构,并支持深层链接和过渡动画等高级特性。
上一篇 下一篇