Head First Android 开发 第三版学习笔记
详见 219 页 6 片段和导航
源码
Navigation
Navigation 组件是 Android Jetpack 的一部分,可以帮助您以一种标准方式实现导航。
Fragment
为什么要使用 Fragment ?
自从发布了 Android Navaigation 组件, Android 团队都推荐改为使用 Fragment。
Navigation 组件主要设计用于 Fragment,所以这也是目前实现 Navigation 的标准方法。
AI 回答
Fragment 是 Android 3.0 版本引入的,它的出现是为了解决 Activity 的复杂性问题。在 Android 3.0 之前,Activity 是 Android 应用的基本组成单元,一个 Activity 通常负责一个屏幕的显示,当应用的功能越来越复杂时,一个 Activity 就会变得越来越庞大,这样会导致 Activity 的代码量过大,不利于代码的维护和扩展。Fragment 的出现就是为了解决这个问题,Fragment 可以看作是一个 Activity 的子 Activity,一个 Activity 可以包含多个 Fragment,每个 Fragment 可以看作是一个独立的模块,这样就可以将 Activity 拆分成多个 Fragment,每个 Fragment 负责一个模块的显示,这样就可以提高代码的复用性和可维护性。
为项目增加 Fragment
New -> Fragment -> Fragment (Blank) -> Fragment Name -> Finish
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| package com.hfad.secretmessage
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.findNavController
class WelcomeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_welcome, container, false)
val startButton = view.findViewById<View>(R.id.start)
startButton.setOnClickListener {
view.findNavController().navigate(R.id.action_welcomeFragment_to_messageFragment)
}
return view
}
}
|
fragment_welcome.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
tools:context=".WelcomeFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="20dp"
android:textSize="20sp"
android:text="@string/welcome_text" />
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/start" />
</LinearLayout>
|
在 FragmentContainerView 中显示 Fragment
activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
| <?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_host_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true"
tools:context=".MainActivity">
</androidx.fragment.app.FragmentContainerView>
|
MainActivity.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
| package com.hfad.secretmessage
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
}
}
|
使用 Navigation 组件在 Fragment 之间导航
片段间的导航包含 3 个主要部分
导航图 (Navigation Graph)
包含应用所需要的所有与导航相关的信息,并且描述了在应用中导航时用户选择的可能路径。
导航图时一个 XML 资源,通常可以在一个可视化设计编辑器中编辑。
导航宿主 (Navigation Host)
一个空容器,用来显示导航目标片段。
要把导航宿主增加到活动的布局。
导航控制器 (Navigation Controller)
控制用户在应用中导航时要在导航宿主中显示哪个片段。
可以用 Kotlin 代码与导航控制器交互。
Gradle 中添加 Navigation 组件
1
2
3
4
5
6
7
| dependencies {
buildscript {
ext.nav_version = "2.3.5"
}
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
}
|
创建导航图 (Navigation Graph)
res -> New -> Android Resource File -> Navigation -> File Name -> OK
向导航图增加片段
使用动作连接片段
导航图是 XML 文件
通常使用设计编辑器编辑
res/navigation/nav_graph.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| <?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/welcomeFragment">
<fragment
android:id="@+id/welcomeFragment"
android:name="com.hfad.secretmessage.WelcomeFragment"
android:label="fragment_welcome"
tools:layout="@layout/fragment_welcome" >
<action
android:id="@+id/action_welcomeFragment_to_messageFragment"
app:destination="@id/messageFragment" />
</fragment>
<fragment
android:id="@+id/messageFragment"
android:name="com.hfad.secretmessage.MessageFragment"
android:label="fragment_message"
tools:layout="@layout/fragment_message" />
</navigation>
|
使用 FragmentContainerView 为布局增加一个导航宿主(Navigation Host)
activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
| <?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_host_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true"
tools:context=".MainActivity">
</androidx.fragment.app.FragmentContainerView>
|
关键代码
android:name="androidx.navigation.fragment.NavHostFragment"
FragmentContainerView 包含一个 NavHostFragmentapp:navGraph
告诉导航宿主要使用哪个导航图app:defaultNavHost="true"
允许导航宿主与设备后退按钮交互
Fragment 不是 Activity 的子类
获得一个导航控制器(Navigation Controller)
每次想导航到一个新片段时,首先需要得到一个导航控制器的引用。需要调用其根 View 对象的 findNavController()
方法。
导航到指定片段
1
2
| view.findNavController()
.navigate(R.id.action_welcomeFragment_to_messageFragment)
|
WelcomeFragment.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| package com.hfad.secretmessage
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.findNavController
class WelcomeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_welcome, container, false)
val startButton = view.findViewById<View>(R.id.start)
startButton.setOnClickListener {
view.findNavController()
.navigate(R.id.action_welcomeFragment_to_messageFragment)
}
return view
}
}
|