Files
flyLink/skylink_android_app/ADVANCED_NAVIGATION.md
flowerstonezl 056289625c init
2026-01-19 17:08:49 +08:00

466 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🗺️ 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<NavigationEvent>()
val navigationEvent: LiveData<NavigationEvent> = _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<NavController>()
}
@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
**状态**: ✅ 完成