8.1.1 传统导航方式的痛点,以及Navigation的优势
传统导航方式的问题:
kotlin
// 传统方式 - 使用 FragmentTransaction 手动管理导航
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 手动添加 Fragment
supportFragmentManager.beginTransaction()
.add(R.id.fragmentContainer, HomeFragment())
.commit()
}
fun navigateToUserDetail(userId: String) {
// 手动管理 Fragment 切换
val fragment = UserDetailFragment()
val args = Bundle()
args.putString("userId", userId)
fragment.arguments = args
supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.addToBackStack("UserDetail")
.commit()
}
fun navigateToSettings() {
// 手动管理 Fragment 切换
supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, SettingsFragment())
.addToBackStack("Settings")
.commit()
}
override fun onBackPressed() {
// 手动处理返回键
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
} else {
super.onBackPressed()
}
}
}
传统方式的痛点:
| 痛点 | 说明 | 影响 |
|---|---|---|
| 代码分散 | 导航逻辑分散在各个地方 | 难以维护和理解 |
| 返回堆栈管理 | 需要手动管理返回堆栈 | 容易出现导航错误 |
| 参数传递 | 需要手动构建 Bundle | 类型不安全 |
| 测试困难 | 导航逻辑与业务逻辑耦合 | 难以单元测试 |
| 深层链接 | 实现复杂 | 需要大量代码 |
kotlin
// 使用 Navigation 组件 - 简化的导航
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 获取 NavController
navController = findNavController(R.id.navHostFragment)
// 设置导航图
navController.setGraph(R.navigation.nav_graph)
}
fun navigateToUserDetail(userId: String) {
// 类型安全的导航
navController.navigate(
R.id.action_homeFragment_to_userDetailFragment,
bundleOf("userId" to userId)
)
}
fun navigateToSettings() {
// 简单的导航
navController.navigate(R.id.action_homeFragment_to_settingsFragment)
}
}
Navigation 组件的优势:
| 优势 | 说明 |
|---|---|
| 可视化导航 | 可视化的导航图,直观展示应用导航结构 |
| 类型安全 | 使用 Safe Args,类型安全的参数传递 |
| 自动管理返回堆栈 | 自动管理 Fragment 的返回堆栈 |
| 深层链接支持** | 轻松实现深层链接 |
| 动画支持 | 内置转场动画支持 |
| 易于测试 | 导航逻辑与业务逻辑分离 |
8.2 Navigation基本结构(目的地、动作)
8.2.1 导航图的基本结构
Navigation Graph XML 结构:
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_graph"
app:startDestination="@id/homeFragment">
<!-- 目的地1:首页 -->
<fragment
android:id="@+id/homeFragment"
android:name="com.example.app.ui.home.HomeFragment"
android:label="Home"
tools:layout="@layout/fragment_home">
<!-- 动作:导航到用户详情页 -->
<action
android:id="@+id/action_homeFragment_to_userDetailFragment"
app:destination="@id/userDetailFragment" />
<!-- 动作:导航到设置页 -->
<action
android:id="@+id/action_homeFragment_to_settingsFragment"
app:destination="@id/settingsFragment" />
</fragment>
<!-- 目的地2:用户详情页 -->
<fragment
android:id="@+id/userDetailFragment"
android:name="com.example.app.ui.user.UserDetailFragment"
android:label="User Detail"
tools:layout="@layout/fragment_user_detail">
<!-- 参数:用户 ID -->
<argument
android:name="userId"
android:defaultValue=""
app:argType="string"
app:nullable="true" />
<!-- 动作:返回首页 -->
<action
android:id="@+id/action_userDetailFragment_to_homeFragment"
app:destination="@id/homeFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />
</fragment>
<!-- 目的地3:设置页 -->
<fragment
android:id="@+id/settingsFragment"
android:name="com.example.app.ui.settings.SettingsFragment"
android:label="Settings"
tools:layout="@layout/fragment_settings">
<!-- 动作:返回首页 -->
<action
android:id="@+id/action_settingsFragment_to_homeFragment"
app:destination="@id/homeFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />
</fragment>
</navigation>
8.2.2 目的地类型
Navigation 支持的目的地类型:
| 类型 | 说明 | 使用场景 |
|---|---|---|
| Fragment | Fragment 目的地 | 最常用的目的地类型 |
| Activity | Activity 目的地 | 跨 Activity 导航 |
| Dialog | Dialog 目的地 | 显示对话框 |
| NavGraph | 嵌套导航图 | 复杂的导航结构 |
不同目的地的示例:
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 目的地 -->
<fragment
android:id="@+id/homeFragment"
android:name="com.example.app.ui.home.HomeFragment"
android:label="Home" />
<!-- Activity 目的地 -->
<activity
android:id="@+id/mainActivity"
android:name="com.example.app.MainActivity"
android:label="Main Activity" />
<!-- Dialog 目的地 -->
<dialog
android:id="@+id/dialogFragment"
android:name="com.example.app.ui.dialog.MyDialogFragment"
android:label="Dialog" />
<!-- 嵌套导航图 -->
<include app:graph="@navigation/nested_graph" />
</navigation>
8.2.3 动作(Action)的配置
动作的属性:
xml
<fragment
android:id="@+id/homeFragment"
android:name="com.example.app.ui.home.HomeFragment"
android:label="Home">
<!-- 基本动作 -->
<action
android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment" />
<!-- 带返回堆栈管理的动作 -->
<action
android:id="@+id/action_home_to_profile"
app:destination="@id/profileFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />
<!-- 带转场动画的动作 -->
<action
android:id="@+id/action_home_to_settings"
app:destination="@id/settingsFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<!-- 单实例导航 -->
<action
android:id="@+id/action_home_to_login"
app:destination="@id/loginFragment"
app:launchSingleTop="true" />
</fragment>