文章目录

  • 一、Navigation 导航组件开发核心要点说明
  • 1、Navigation 各子部件的创建顺序
  • 2、导入 Navigation 依赖
  • 3、创建 Fragment 及布局文件
  • 4、创建 Navigation Graph
  • 5、创建 NavHostFragment
  • 6、创建 NavController
  • 7、Fragment 实现导航操作
  • 二、Navigation 导航组件完整代码示例
  • 1、Fragment 完整源码
  • FragmentA 源码
  • FragmentA 布局文件
  • FragmentB 源码
  • FragmentB 布局文件
  • 2、Navigation Graph 源码
  • 3、Activity 布局文件中添加 NavHostFragment 组件
  • 4、Activity 代码中获取 NavController 并进行导航
  • 5、运行效果




代码地址 :





一、Navigation 导航组件开发核心要点说明




1、Navigation 各子部件的创建顺序



Navigation 导航组件各子部件的创建顺序要点如下 :

  • 首先 , 创建被导航的 Fragment 页面代码 和 对应的布局文件 ;
  • 然后 , 基于创建的 Fragment 创建对应的 Navigation Graph , 在其中配置 Fragment 之间的跳转动作 ;
  • 再后 , 基于创建的 Navigation Graph 创建显示内容的 NavHostFragment 组件 ;
  • 最后 , 在 Activity 中 获取 NavController 组件实现 Fragment 之间的跳转 ;

必须按照上述流程进行创建 : Fragment -> Navigation Graph -> NavHostFragment -> NavController , 后面的组件依赖于前面的组件 , 否则无法实现 Navigation 导航 ;



2、导入 Navigation 依赖



使用 Navigation 组件 , 必须导入 Navigation 依赖 , 不同的开发语言导入不同的依赖 :

  • 使用 Java 语言 开发 Navigation 导航 , 导入如下依赖 :
dependencies {  
    implementation 'androidx.navigation:navigation-fragment:2.2.2'
    implementation 'androidx.navigation:navigation-ui:2.2.2'
}
  • 使用 Kotlin 语言 开发 Navigation 导航 , 导入如下依赖 :
dependencies {  
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'  
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'  
}

如果不导入依赖 , 直接使用 Navigation , 会报出 " failed to add navigation dependency " 异常信息 ;



3、创建 Fragment 及布局文件



右键点击 要创建 Fragment 的代码 Package 包 , 选择 " New / Fragment / Fragment (Blank) " 选项 , 就可以直接 创建一个 Fragment , 同时会生成对应的 Fragment 布局文件 ;

【Jetpack】Navigation 导航组件 ② ( Navigation 核心要点说明 | 创建 Navigation Graph | 创建 NavHostFragment | 完整代码示例 )_NavHostFragment



4、创建 Navigation Graph



右键点击 res 资源目录 , 在弹出的下拉菜单中 , 选择 " New / Android Resource File " 选项 ,

【Jetpack】Navigation 导航组件 ② ( Navigation 核心要点说明 | 创建 Navigation Graph | 创建 NavHostFragment | 完整代码示例 )_Jetpack_02


在 " Resource Type " 选项处 , 选择 " Navigation " 选项 , 选择后 " Root element " 和 " Directory name " 选项会被自动设置 , 开发者只需要设置一个 " File name " 即可 ;

【Jetpack】Navigation 导航组件 ② ( Navigation 核心要点说明 | 创建 Navigation Graph | 创建 NavHostFragment | 完整代码示例 )_Jetpack_03


创建 Navigation Graph 时 , 需要指定要将哪些 Fragment 纳入导航管理 , 这也是要先创建 Fragment , 然后才能创建 Navigation Graph 的原因 ;

创建完 Navigation Graph 后 , 在 Design 模式下 , 点击 " New Destination " 按钮 , 添加要进行导航的 Fragment 页面 , 这里将 FragmentA 和 FragmentB 都纳入到 Navigation 导航管理中 ;

【Jetpack】Navigation 导航组件 ② ( Navigation 核心要点说明 | 创建 Navigation Graph | 创建 NavHostFragment | 完整代码示例 )_NavHostFragment_04

创建后可以通过拖动鼠标 , 设置 Navigation Graph 中两个 Fragment 之间的跳转关系 , FragmentA 跳转到 FragmentB , FragmentB 跳转到 FragmentA ;

【Jetpack】Navigation 导航组件 ② ( Navigation 核心要点说明 | 创建 Navigation Graph | 创建 NavHostFragment | 完整代码示例 )_NavController_05

这样就完成了 Navigation Graph 的创建 , 之后会通过 NavController 调用上面的两个跳转 , 分别实现两个 Fragment 之间的跳转 ;



5、创建 NavHostFragment



NavHostFragment 设置在 Activity 的布局文件中 , 一般是为 <fragment/>标签设置一个 android:name="androidx.navigation.fragment.NavHostFragment" 属性 , 该 fragment 就成为了 NavHostFragment ;

<fragment
        android:id="@+id/fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/navigation_graph" />

这里不建议在 Design 图形化界面中拖动 Container 下的 NavHostFragment 到布局中 , 生成的代码是错误的 ;

具体出错的内容在错误记录专栏有详细分析 , 这里不再赘述 ;

【Jetpack】Navigation 导航组件 ② ( Navigation 核心要点说明 | 创建 Navigation Graph | 创建 NavHostFragment | 完整代码示例 )_Jetpack_06



6、创建 NavController



在 Activity 中通过 调用 findNavController 函数 , 获取 NavController , 然后通过该 NavController 变量进行导航 ;

fragmentContainerView 组件的 管理 操作通过 NavController 完成 ;

// fragmentContainerView 组件的 管理 操作通过 NavController 完成
        // 对应的就是 navController 实例变量
        val navController = findNavController(this, R.id.fragment)
        NavigationUI.setupActionBarWithNavController(this, navController)



7、Fragment 实现导航操作



在 Fragment 界面中 , 通过点击按钮 , 跳转到另外一个 Fragment 界面 ;

首先 , 获取 NavigationController , 通过调用 Navigation.findNavController 函数 , 即可获取 NavigationController 实例对象 ;

然后 , 调用 NavigationController#navigate 进行导航 , 传入 Navigation Graph 中具体的 action 的 id 即可 ;

// 获取 NavigationController
            val navController = Navigation.findNavController(it)
            // 按照 action_fragmentA_to_fragmentB 对应的 action 的导航路线走
            navController.navigate(R.id.action_fragmentA_to_fragmentB)

上述代码中 R.id.action_fragmentA_to_fragmentB 对应的 action 如下 :

<fragment
        android:id="@+id/fragmentA"
        android:name="kim.hsl.nav.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" >
        <action
            android:id="@+id/action_fragmentA_to_fragmentB"
            app:destination="@id/fragmentB" />
    </fragment>






二、Navigation 导航组件完整代码示例



代码地址 :


1、Fragment 完整源码



创建 2 个 Fragment , 每个 Fragment 都设置一个按钮 , 两个 Fragment 之间互相跳转 ;

FragmentA 源码

Fragment 中核心的跳转源码如下 , 先获取 NavigationController , 然后调用 NavigationController#navigate 跳转到 FragmentB 页面 ;

// 获取 NavigationController
            val navController = Navigation.findNavController(it)
            // 按照 action_fragmentA_to_fragmentB 对应的 action 的导航路线走
            navController.navigate(R.id.action_fragmentA_to_fragmentB)



完整源码 :

package kim.hsl.nav

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import androidx.navigation.Navigation.findNavController

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
 * A simple [Fragment] subclass.
 * Use the [FragmentA.newInstance] factory method to
 * create an instance of this fragment.
 */
class FragmentA : Fragment() {
    // TODO: Rename and change types of parameters
    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_a, container, false)
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment FragmentA.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            FragmentA().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val button = view.findViewById<Button>(R.id.button)
        button.setOnClickListener {
            // 获取 NavigationController
            val navController = Navigation.findNavController(it)
            // 按照 action_fragmentA_to_fragmentB 对应的 action 的导航路线走
            navController.navigate(R.id.action_fragmentA_to_fragmentB)
        }
    }
}

FragmentA 布局文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FragmentA">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_blank_fragment" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="跳转到 B"
        android:onClick="onClick" />

</FrameLayout>

FragmentB 源码

Fragment 中核心的跳转源码如下 , 先获取 NavigationController , 然后调用 NavigationController#navigate 跳转到 FragmentB 页面 ;

// 获取 NavigationController
            val navController = Navigation.findNavController(it)
            // 按照 action_fragmentB_to_fragmentA 对应的 action 的导航路线走
            navController.navigate(R.id.action_fragmentB_to_fragmentA)



完整代码如下 :

package kim.hsl.nav

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.navigation.Navigation

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
 * A simple [Fragment] subclass.
 * Use the [FragmentB.newInstance] factory method to
 * create an instance of this fragment.
 */
class FragmentB : Fragment() {
    // TODO: Rename and change types of parameters
    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_b, container, false)
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment FragmentB.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            FragmentB().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val button = view.findViewById<Button>(R.id.button)
        button.setOnClickListener {
            // 获取 NavigationController
            val navController = Navigation.findNavController(it)
            // 按照 action_fragmentB_to_fragmentA 对应的 action 的导航路线走
            navController.navigate(R.id.action_fragmentB_to_fragmentA)
        }
    }
}

FragmentB 布局文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FragmentB">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_blank_fragment" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="跳转到 A"
        android:onClick="onClick" />

</FrameLayout>



2、Navigation Graph 源码



创建 Navigation Graph , 导入两个 Fragment , 并设置 Navigation Graph 中两个 Fragment 之间的跳转关系 , FragmentA 跳转到 FragmentB , FragmentB 跳转到 FragmentA ;

<?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/navigation_graph"
    app:startDestination="@id/fragmentA">

    <fragment
        android:id="@+id/fragmentA"
        android:name="kim.hsl.nav.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" >
        <action
            android:id="@+id/action_fragmentA_to_fragmentB"
            app:destination="@id/fragmentB" />
    </fragment>
    <fragment
        android:id="@+id/fragmentB"
        android:name="kim.hsl.nav.FragmentB"
        android:label="fragment_b"
        tools:layout="@layout/fragment_b" >
        <action
            android:id="@+id/action_fragmentB_to_fragmentA"
            app:destination="@id/fragmentA" />
    </fragment>
</navigation>



3、Activity 布局文件中添加 NavHostFragment 组件



NavHostFragment 设置在 Activity 的布局文件中 , 一般是为 <fragment/>标签设置一个 android:name="androidx.navigation.fragment.NavHostFragment" 属性 , 该 fragment 就成为了 NavHostFragment ;

<fragment
        android:id="@+id/fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/navigation_graph" />

完整布局文件代码如下 :

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <fragment
        android:id="@+id/fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/navigation_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>



4、Activity 代码中获取 NavController 并进行导航



在 Activity 中通过 调用 findNavController 函数 , 获取 NavController , 然后通过该 NavController 变量进行导航 ;

fragmentContainerView 组件的 管理 操作通过 NavController 完成 ;

// fragmentContainerView 组件的 管理 操作通过 NavController 完成
        // 对应的就是 navController 实例变量
        val navController = findNavController(this, R.id.fragment)
        NavigationUI.setupActionBarWithNavController(this, navController)

完整代码如下 :

package kim.hsl.nav

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.Navigation.findNavController
import androidx.navigation.ui.NavigationUI

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // fragmentContainerView 组件的 管理 操作通过 NavController 完成
        // 对应的就是 navController 实例变量
        val navController = findNavController(this, R.id.fragment)
        NavigationUI.setupActionBarWithNavController(this, navController)
    }
}



5、运行效果



在 FragmentA 页面点击按钮 , 跳转到 FragmentB 页面 , 在 FragmentB 页面点击按钮 , 跳转到 FragmentA 页面 ;

【Jetpack】Navigation 导航组件 ② ( Navigation 核心要点说明 | 创建 Navigation Graph | 创建 NavHostFragment | 完整代码示例 )_NavHostFragment_07



代码地址 :