Android Fragment Navigation

Head First Android 开发 第三版学习笔记

详见 219 页 6 片段和导航 源码

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 包含一个 NavHostFragment
  • app: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
    }
}
comments powered by Disqus