# 🗺️ Navigation 高级用法指南 ## 📌 Overview 本文档展示如何使用高级的 Navigation 扩展和工具类来优雅地管理应用导航。 --- ## 🎯 核心概念 ### 1. Routes 常量对象 ```kotlin object Routes { const val HOME = "home" const val SETTINGS = "settings" } ``` ### 2. NavigationEvent 密封类 ```kotlin sealed class NavigationEvent { object NavigateToHome : NavigationEvent() object NavigateToSettings : NavigationEvent() object NavigateUp : NavigationEvent() data class NavigateToRoute(val route: String) : NavigationEvent() } ``` ### 3. NavigationManager 工具类 提供统一的导航操作接口,包括: - 安全导航 (防止重复点击) - 带 Back Stack 清理的导航 - 事件处理 --- ## 💻 代码示例 ### 基础导航 **在 Activity 中**: ```kotlin class MainActivity : AppCompatActivity() { private lateinit var navController: NavController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setupNavigation() } private fun setupNavigation() { val navHostFragment = supportFragmentManager .findFragmentById(R.id.nav_host_fragment) as NavHostFragment navController = navHostFragment.navController } } ``` ### 快速导航方法 **方式 1:使用 NavController 扩展** ```kotlin // 快速导航到首页 navController.navigateToHome() // 快速导航到设置 navController.navigateToSettings() ``` **方式 2:使用 NavigationManager** ```kotlin // 安全导航 NavigationManager.navigateSafe(navController, Routes.HOME) // 导航并清理 Back Stack NavigationManager.navigateWithClear( navController, Routes.SETTINGS, popUpTo = Routes.HOME, inclusive = false ) ``` **方式 3:处理导航事件** ```kotlin val event = NavigationEvent.NavigateToSettings NavigationManager.handleNavigationEvent(navController, event) ``` ### 在 Fragment 中使用 **SettingsFragment 中导航**: ```kotlin class SettingsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val navController = findNavController() // 导航回首页 someButton.setOnClickListener { navController.navigateToHome() } } } ``` ### 在 ViewModel 中使用 **使用 LiveData 和导航事件**: ```kotlin class AppViewModel : ViewModel() { private val _navigationEvent = MutableLiveData() val navigationEvent: LiveData = _navigationEvent fun goToSettings() { _navigationEvent.value = NavigationEvent.NavigateToSettings } fun goBack() { _navigationEvent.value = NavigationEvent.NavigateUp } } ``` **在 Fragment 中观察**: ```kotlin class HomeFragment : Fragment() { private val viewModel: AppViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.navigationEvent.observe(viewLifecycleOwner) { event -> val navController = findNavController() NavigationManager.handleNavigationEvent(navController, event) } } } ``` --- ## 🛡️ 防止重复导航 ### 问题描述 快速点击按钮可能导致多次导航,造成 back stack 混乱。 ### 解决方案 **使用 NavigationManager.navigateSafe()**: ```kotlin // ❌ 不安全 - 可能重复导航 button.setOnClickListener { navController.navigate(R.id.settingsFragment) } // ✅ 安全 - 检查当前目的地 button.setOnClickListener { navController.navigateSafe(Routes.SETTINGS) } ``` **原理**: ```kotlin fun navigateSafe(navController: NavController, route: String) { try { if (navController.currentDestination?.route != route) { navController.navigate(route) { launchSingleTop = true // 防止堆栈多个实例 } } } catch (e: Exception) { e.printStackTrace() } } ``` --- ## 🔄 Back Stack 管理 ### 清理 Back Stack **场景**:用户登录后不应返回到登录页 ```kotlin // 导航到首页,并清理 Back Stack navController.navigateAndClear( route = Routes.HOME, popUpTo = Routes.SETTINGS // 清理此位置及以下 ) // 等价于 NavigationManager.navigateWithClear( navController, Routes.HOME, popUpTo = Routes.SETTINGS, inclusive = true ) ``` ### 完整示例 ```kotlin // 登录成功后导航到首页,并清理登录页 private fun handleLoginSuccess() { navController.navigate(Routes.HOME) { // 清理所有登录相关的 Fragment popUpTo(Routes.LOGIN) { inclusive = true // 包含登录页本身 } launchSingleTop = true } } ``` --- ## 🎯 NavigationHelper 使用 **在 MainActivity 中初始化**: ```kotlin class MainActivity : AppCompatActivity() { private lateinit var navController: NavController private lateinit var navigationHelper: NavigationHelper override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navHostFragment = supportFragmentManager .findFragmentById(R.id.nav_host_fragment) as NavHostFragment navController = navHostFragment.navController navigationHelper = NavigationHelper(navController) } override fun onBackPressed() { if (!navigationHelper.handleBackPressed()) { @Suppress("DEPRECATION") super.onBackPressed() } } } ``` **功能方法**: ```kotlin // 获取当前路由 val currentRoute = navigationHelper.getCurrentRoute() // 检查是否在特定页面 if (navigationHelper.isAtTopDestination(Routes.HOME)) { // 在首页 } // 处理后退 navigationHelper.handleBackPressed() ``` --- ## 📊 导航流程图 ``` ┌─────────────────────┐ │ NavigationEvent │ │ (密封类定义事件) │ └──────────┬──────────┘ │ ┌──────▼──────┐ │ NavigationExt │ │ (扩展方法) │ └──────┬──────┘ │ ┌──────▼──────────────┐ │ NavigationManager │ │ (工具类处理逻辑) │ └──────┬──────────────┘ │ ┌──────▼──────────────┐ │ NavController │ │ (执行导航) │ └─────────────────────┘ ``` --- ## 🔀 高级导航场景 ### 场景 1:带参数的导航 ```kotlin // 定义路由 const val DETAIL = "detail/{id}" const val DETAIL_ROUTE = "detail" // 导航时传递参数 navController.navigate("$DETAIL_ROUTE/123") // 在 DetailFragment 中接收 val id = arguments?.getString("id") ``` ### 场景 2:条件导航 ```kotlin fun navigateBasedOnUserState(user: User?) { when { user == null -> navController.navigateSafe(Routes.LOGIN) user.isProfileComplete -> navController.navigateSafe(Routes.HOME) else -> navController.navigateSafe(Routes.SETUP_PROFILE) } } ``` ### 场景 3:链式导航 ```kotlin // 多步导航 fun performLoginFlow() { // 第 1 步:导航到验证码页 navController.navigate(Routes.VERIFICATION) // 验证成功后,导航到主页 // (通过 ViewModel 事件驱动) } ``` --- ## ⚙️ 扩展 NavigationEvent ### 添加新的导航事件 ```kotlin sealed class NavigationEvent { // 现有事件... object NavigateToHome : NavigationEvent() object NavigateToSettings : NavigationEvent() // 新增事件 data class NavigateWithData( val route: String, val data: Bundle ) : NavigationEvent() data class NavigateToDetail(val id: String) : NavigationEvent() } ``` ### 处理新事件 ```kotlin fun NavigationManager.handleNavigationEvent(navController: NavController, event: NavigationEvent) { when (event) { // 现有处理... is NavigationEvent.NavigateWithData -> { navController.navigate(event.route, event.data) } is NavigationEvent.NavigateToDetail -> { val route = "${Routes.DETAIL_ROUTE}/${event.id}" navigateSafe(navController, route) } } } ``` --- ## 🧪 测试导航 ### 单元测试 ```kotlin @RunWith(RobolectricTestRunner::class) class NavigationManagerTest { private lateinit var navController: NavController @Before fun setUp() { navController = mockk() } @Test fun testNavigateSafe() { NavigationManager.navigateSafe(navController, Routes.HOME) verify { navController.navigate(Routes.HOME, any()) } } } ``` ### 集成测试 ```kotlin @RunWith(AndroidJUnit4::class) class NavigationIntegrationTest { @get:Rule val activityRule = ActivityScenarioRule(MainActivity::class.java) @Test fun testNavigationToSettings() { onView(withId(R.id.settings_button)).perform(click()) onView(withId(R.id.settings_fragment)).check( matches(isDisplayed()) ) } } ``` --- ## 📚 最佳实践 ### ✅ 推荐做法 1. **使用 NavigationManager** - 集中管理导航逻辑 2. **创建扩展方法** - 为常见操作创建快捷方式 3. **使用 ViewModel** - 将导航事件与业务逻辑分离 4. **安全导航** - 始终使用 navigateSafe() 5. **清理 Back Stack** - 适当时清理堆栈 ### ❌ 避免做法 1. **直接调用 navigate()** - 容易出现重复导航 2. **硬编码路由** - 使用常量对象 3. **混合导航方式** - 统一使用扩展或工具类 4. **忘记处理异常** - 始终 try-catch 5. **不清理 Back Stack** - 导致内存泄漏 --- ## 📞 快速参考 | 操作 | 方法 | |------|------| | 导航到首页 | `navController.navigateToHome()` | | 导航到设置 | `navController.navigateToSettings()` | | 安全导航 | `navController.navigateSafe(route)` | | 清理导航 | `navController.navigateAndClear(route)` | | 处理事件 | `NavigationManager.handleNavigationEvent()` | | 后退 | `navController.navigateUp()` | | 获取当前路由 | `navigationHelper.getCurrentRoute()` | --- ## 📖 相关文档 - [Android Navigation Docs](https://developer.android.com/guide/navigation) - [Navigation Component Architecture](https://developer.android.com/guide/navigation/navigation-getting-started) --- **完成日期**: 2026-01-19 **版本**: 1.0 **状态**: ✅ 完成