Android中Navigation使用介绍
一、Navigation基础使用
1. 添加依赖 (app/build.gradle)
dependencies {def nav_version = "2.7.7"implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"implementation "androidx.navigation:navigation-ui-ktx:$nav_version"// 可选:Safe Args 插件(类型安全参数传递)apply plugin: "androidx.navigation.safeargs.kotlin"
}
2. 创建导航图 (res/navigation/nav_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/nav_main"app:startDestination="@id/homeFragment"> <!-- 固定入口 --><!-- 首页 --><fragmentandroid:id="@+id/homeFragment"android:name="com.example.app.HomeFragment"android:label="Home"tools:layout="@layout/fragment_home"><!-- 定义跳转动作 --><actionandroid:id="@+id/action_home_to_detail"app:destination="@id/detailFragment" /></fragment><!-- 详情页 --><fragmentandroid:id="@+id/detailFragment"android:name="com.example.app.DetailFragment"android:label="Detail"tools:layout="@layout/fragment_detail"><!-- 定义接收的参数 --><argumentandroid:name="itemId"app:argType="integer" /></fragment>
</navigation>
3. 设置导航宿主 (activity_main.xml)
<androidx.fragment.app.FragmentContainerViewandroid:id="@+id/nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"app:defaultNavHost="true" <!-- 关键:拦截返回键 -->app:navGraph="@navigation/nav_main" /> <!-- 绑定导航图 -->
4. 执行导航 (HomeFragment.kt)
class HomeFragment : Fragment() {private lateinit var binding: FragmentHomeBindingoverride fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {binding = FragmentHomeBinding.inflate(inflater, container, false)return binding.root}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// 获取 NavControllerval navController = findNavController()binding.btnGoToDetail.setOnClickListener {// 方式1:基础导航// navController.navigate(R.id.action_home_to_detail)// 方式2:带参数导航(Bundle)val args = Bundle().apply {putInt("itemId", 12345)}navController.navigate(R.id.action_home_to_detail, args)// 方式3:Safe Args(推荐)// val direction = HomeFragmentDirections.actionHomeToDetail(itemId = 12345)// navController.navigate(direction)}}
}
5. 接收参数 (DetailFragment.kt)
class DetailFragment : Fragment() {// 传统方式获取参数private val itemId by lazy { arguments?.getInt("itemId", -1) ?: -1 }// Safe Args方式(需在build.gradle应用插件)// private val args: DetailFragmentArgs by navArgs()// val itemId = args.itemIdoverride fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)Log.d("DetailFragment", "Received itemId: $itemId")}
}
二、核心原理:Navigation组成架构
1. NavGraph(导航图)
-
作用:应用导航的"地图"
-
存储内容:
-
所有目的地(Destination)
-
动作(Action)定义
-
参数(Argument)声明
-
-
编译时:XML 被解析为
NavGraph
对象
2. NavHost(导航宿主)
-
实现类:通常是
NavHostFragment
-
职责:
-
提供 Fragment 容器
-
响应 NavController 指令
-
管理 Fragment 切换
-
-
关键特性:
app:defaultNavHost="true" <!-- 拦截返回键 -->
3. NavController(导航控制器)
-
获取方式:
// Fragment中 findNavController() // Activity中 Navigation.findNavController(activity, R.id.nav_host_fragment)
-
核心职责:
-
执行导航操作
-
管理返回栈(BackStack)
-
协调 Navigator
-
三、NavController 核心 API 详解
1. navigate(int resId, Bundle args, NavOptions navOptions)
-
作用:导航到指定目的地
-
参数:
-
resId
:目标页面ID(如R.id.detailFragment
) -
args
:传递参数的 Bundle -
navOptions
:导航行为配置
-
-
特点:
-
默认将新页面加入回退栈(可通过
navOptions
修改) -
参数传递:使用 Safe Args 插件可类型安全传参
-
生命周期影响:目标 Fragment 会经历完整生命周期
-
-
示例:
// 带参数导航
val args = bundleOf("itemId" to 123)
findNavController().navigate(R.id.detailFragment, args)// 使用 NavOptions 配置
val options = NavOptions.Builder().setLaunchSingleTop(true) // 启用单例模式(但仍有重建问题).setEnterAnim(R.anim.slide_in).build()
findNavController().navigate(R.id.detailFragment, null, options)
2. popBackStack(int destinationId, boolean inclusive)
-
作用:回退到指定页面
-
参数:
-
destinationId
:目标回退页面ID -
inclusive
:是否同时弹出目标页面本身
-
-
特点:
-
参数保留:回退后栈中页面保留原有参数
-
清栈行为:当
inclusive=true
时,目标页面也被移除 -
返回值:成功时返回
true
,若目标页不在栈中返回false
-
-
示例:
// 回退到首页(保留首页)
findNavController().popBackStack(R.id.homeFragment, false) // 清空回退栈直到登录页(包括登录页)
findNavController().popBackStack(R.id.loginFragment, true)
3. navigateUp()
-
作用:向上导航(遵循导航图层级)
-
特点:
-
优先使用导航图父节点,而非物理返回栈
-
返回值:成功返回
true
,无父级时返回false
-
典型场景:处理 ActionBar 的向上箭头
-
-
生命周期:当前页面被销毁,目标页面重建
-
示例:
// 在 Activity 中处理向上导航
override fun onSupportNavigateUp(): Boolean {return findNavController(R.id.nav_host_fragment).navigateUp() || super.onSupportNavigateUp()
}
4. 深度链接
<fragment android:id="@+id/detailFragment"><deepLink app:uri="https://example.com/detail/{itemId}" />
</fragment>
// 处理Intent跳转
val deepLinkIntent = Intent(Intent.ACTION_VIEW, "https://example.com/detail/123".toUri())
findNavController().handleDeepLink(deepLinkIntent)
四、Navigation 的缺点及解决方案
1. 静态配置维护困难
-
问题:所有页面需在 XML 预定义,大型项目修改成本高
-
案例:添加新页面需修改
nav_graph.xml
,可能引发冲突
2. 固定启动页不灵活
-
问题:
startDestination
必须静态指定 -
影响:无法根据条件(如登录状态)动态设置
3. Fragment 频繁重建
-
原因:默认
FragmentNavigator
使用replace()
-
影响:
-
页面返回时
onCreateView()
重新执行 -
复杂视图性能开销大
-
状态丢失需手动保存
-
4. 无效的 singleTop 模式
-
问题:
app:launchSingleTop="true"
仍会重建栈顶页面 -
原因:未实现真正的实例复用