【Jetpack】Navigation navigation component ⑤ (used by NavigationUI class)


Code address:


The NavigationUI class supports some system-built controls. After configuration, the function of automatically jumping to the Fragment interface is very simple to use. The supported configurable Navigation jumping controls are:

  • ActionBar
  • ToolBar
  • CollapsingToolbarLayout
  • Menu

In Android development, NavigationUI is an important tool for building and managing application navigation interfaces, which can greatly improve development efficiency;





1. Introduction to NavigationUI class



NavigationUI is a component officially provided by Google for managing Navigation navigation, which belongs to the Jetpack toolkit of the Android system;

With the help of NavigationUI, it is very convenient to create and organize the navigation interface of the application;

Such as: building complex navigation structures, vertical or horizontal main menus, sidebars, navigation drawers, etc.;


Developers can easily manage page transitions and navigation through NavigationUI;

NavigationUI provides some static methods to handle interface navigation in the top application bar/drawer navigation bar/bottom navigation bar;


This blog introduces a usage scenario: use the menu options in the AppBar to control the navigation interface jump;

In the Activity, when using the Navigation component to switch the Fragment interface, in addition to interface switching, the top title bar AppBar corresponding to different interfaces needs to be changed accordingly;

In the Navigation component, the NavigationUI class is provided to uniformly manage UI changes related to Fragment page switching;





2. NavigationUI class usage process



This chapter introduces the process of using the menu options in the AppBar to control the navigation interface jump;


1. Create Fragment


Create two Fragments, respectively as two interfaces to jump to each other;

Right click on the package name, select "New / Fragment / Fragment (Blank)" option,

insert image description here

In the pop-up interface, enter the Fragment name to create a new Fragment;

insert image description here

When Fragment is created, the corresponding layout file will be automatically generated:

insert image description here


Note: This operation is tricky. When generating a Fragment, the Gradle dependency of the Kotlin language plug-in will be automatically added, and the dependency can be deleted if necessary; the automatically generated dependency does not configure the Maven source, and an error will be reported;

buildscript {
    
    
    dependencies {
    
    
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0-RC2'
    }
}

2. Create NavigationGraph


After the Fragment is created, the NavigationGraph can be created, otherwise there is no corresponding Fragment option;

Create a NavigationGraph,

Right-click the res directory, select the "New / Android Resource File" option,

insert image description here

In the pop-up "New Resource File" dialog box, set the file name, resource type, directory name;

insert image description here

Next, start editing NavigationGraph. After entering Design mode, you will be prompted that Design editor is unavailable until after a successful project sync. This is because the first compilation has not been performed after Fragment is created.

insert image description here

Select "Menu Bar / Build / Make Project" option to compile the application once;

insert image description here

After the compilation is successful, you can use the Design mode of NavigationGraph;

insert image description here

Click the "New Destination" button to add two Fragments to the NavigationGraph;

insert image description here

Add two Fragments to NavigationGraph, no other configuration is required;

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

    <fragment
        android:id="@+id/fragmentA"
        android:name="kim.hsl.app2.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" />
    <fragment
        android:id="@+id/fragmentB"
        android:name="kim.hsl.app2.FragmentB"
        android:label="fragment_b"
        tools:layout="@layout/fragment_b" />
</navigation>

insert image description here

Set the default Fragment, in the root tag navigation,

app:startDestination="@id/fragmentA"

Configuration, which is used to configure the default initial Fragment;


3. Activity imports NavHostFragment


Enter the layout of the MainActivity of the Launcher interface, and delete other elements in the layout;

Drag NavHostFragment into the layout,
insert image description here

After dragging the space into the layout, release the mouse, the following "Navigation Graph" dialog box will pop up, select the NavigationGraph to be dragged in, and then click the "OK" button in the lower right corner;

insert image description here

After dragging in, add constraints to the space, and the final source code is as follows:

<?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: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" />-->

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragmentContainerView"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/my_navigation_graph"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"  />
    
</androidx.constraintlayout.widget.ConstraintLayout>

4. Create a menu


Right-click the res directory, select the "New / Android Resource File" option,

insert image description here

In the pop-up "New Resource File" dialog box, select the resource type as the Menu menu type, and the directory name as menu, then enter the file name, click the "OK" button, and create the menu;

insert image description here

After the creation is complete, in the menu configuration file, the configuration android:id="@+id/fragmentB"means to jump to the Fragment interface corresponding to the id of fragmentB in the NavigationGraph, that is, to jump to the FragmentB interface, trigger the menu option, and jump to the FragmentB interface ;

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/fragmentB"
        android:icon="@mipmap/ic_launcher"
        android:title="跳转到 Fragment B"/>
</menu>

5. The main logic of the Activity interface development NavigationUI (focus)



a. Add Fragment layout


In the layout file of the Activity, add the following code. It is not recommended to drag the NavHostFragment directly on the Design interface, and the generated code will report an error. Just copy the code below;

Just app:navGraph="@navigation/my_navigation_graph"modify it to the NavigationGraph you created yourself;

The name attribute must be set to android:name="androidx.navigation.fragment.NavHostFragment"style;

    <fragment
        android:id="@+id/fragmentContainerView"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:navGraph="@navigation/my_navigation_graph"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"  />

b. Processing Navigation navigation logic (focus)


First, get NavController from the Fragment layout defined in the layout file;

        // 1. 获取 NavController
        navController = Navigation.findNavController(this, R.id.fragmentContainerView)

Then, create AppBarConfiguration, which is an important parameter for binding Navigation and AppBar later;

        // 2. 创建 AppBarConfiguration
        appBarConfiguration = AppBarConfiguration.Builder(navController.graph).build()

Then, associate the Navigation navigation with the AppBar, and after the association, you can use the menu options to jump to the interface;

        // 3. 将 Navigation 导航 与 AppBar 进行关联
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration)

Finally, monitor the switching status of the Navigation page. If the interface is switched through Navigation, the onDestinationChanged function of the DestinationChangedListener listener will be triggered;

        // 4. 监听页面切换状态
        navController.addOnDestinationChangedListener{
    
    
                /*
                    相当于重写了下面的函数
                    public fun onDestinationChanged(
                        controller: NavController,
                        destination: NavDestination,
                        arguments: Bundle?)
                 */
                navController: NavController, navDestination: NavDestination, bundle: Bundle? ->
          

c. Enable menu options


Rewrite the onCreateOptionsMenu method of Activity to load the menu in the resource file,

At this time, only the menu is loaded, and you cannot jump at this time, you need to rewrite the onOptionsItemSelected method;

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    
    
        super.onCreateOptionsMenu(menu)
        // 加载资源文件中的菜单
        // 只加载菜单 , 此时不能跳转 , 需要重写 onOptionsItemSelected 方法才可以
        menuInflater.inflate(R.menu.my_menu, menu)
        return true
    }

d. Use the menu for NavigationUI navigation


It is not enough to only display the menu, but also need to set the behavior of the menu, rewrite this method, the menu options can take effect,

NavigationUI.onNavDestinationSelected(item, navController)The meaning of the code is: use NavigationUI first for navigation,

If the jump fails, then use the traditional way to perform the default action, the code is super.onOptionsItemSelected(item);

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
    
    
        // 重写了该方法 , 菜单选项才能生效
        // 优先使用 NavigationUI 进行导航 , 如果跳转失败 , 再使用传统的方式
        return NavigationUI.onNavDestinationSelected(item, navController)
                || super.onOptionsItemSelected(item)
    }

e. Support navigation back function


By default, through Navigation navigation, you cannot return after jumping to FragmentB. If you want to return, you need to override the onSupportNavigateUp method;

NavigationUI.navigateUp(navController, appBarConfiguration)The role of is to record the Navigation navigation switching interface into the rollback stack;

    // 默认状态下进入 FragmentB 后是无法返回的
    // 如果想要返回, 需要重写 onSupportNavigateUp 方法
    override fun onSupportNavigateUp(): Boolean {
    
    
        return NavigationUI.navigateUp(navController, appBarConfiguration)
                || super.onSupportNavigateUp()
    }




3. Complete source code example - use of NavigationUI class




1. Fragment related source code



a. Kotlin source code and layout file of FragmentA interface


FragmentA is basically the default code, which deletes the logic related to parameter passing, making it more concise;


FragmentA code:

package kim.hsl.app2

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

class FragmentA : Fragment() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
    }

    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)
    }
}

layout file:

<?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="FragmentA" />

</FrameLayout>

b. Kotlin source code and layout files of the FragmentB interface


In this interface, the function of hiding the menu button in the AppBar is set. After jumping to FragmentB, the menu button will no longer be displayed in the upper right corner;


FragmentB code:

package kim.hsl.app2

import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment

class FragmentB : Fragment() {
    
    

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
    
    
        // 确保 onCreateOptionsMenu 函数执行
        setHasOptionsMenu(true)
        // 为 Fragment 加载布局
        return inflater.inflate(R.layout.fragment_b, container, false)
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
    
    
        // 清空菜单
        menu.clear()
        super.onCreateOptionsMenu(menu, inflater)
    }
}

FragmentB's layout file:

<?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="FragmentB" />

</FrameLayout>

2. NavigationGraph related source code


Configure two Fragments here, no need to configure the jump action, just define the Fragment here;

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

    <fragment
        android:id="@+id/fragmentA"
        android:name="kim.hsl.app2.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" />
    <fragment
        android:id="@+id/fragmentB"
        android:name="kim.hsl.app2.FragmentB"
        android:label="fragment_b"
        tools:layout="@layout/fragment_b" />
</navigation>

3. MainActivity main interface related source code (key point)


package kim.hsl.app2

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.Navigation
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.NavigationUI

class MainActivity : AppCompatActivity() {
    
    

    lateinit var navController: NavController
    lateinit var appBarConfiguration: AppBarConfiguration

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

        // 1. 获取 NavController
        navController = Navigation.findNavController(this, R.id.fragmentContainerView)

        // 2. 创建 AppBarConfiguration
        appBarConfiguration = AppBarConfiguration.Builder(navController.graph).build()

        // 3. 将 Navigation 导航 与 AppBar 进行关联
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration)

        // 4. 监听页面切换状态
        navController.addOnDestinationChangedListener{
    
    
                /*
                    相当于重写了下面的函数
                    public fun onDestinationChanged(
                        controller: NavController,
                        destination: NavDestination,
                        arguments: Bundle?)
                 */
                navController: NavController, navDestination: NavDestination, bundle: Bundle? ->
                Log.i("octopus", "OnDestinationChangedListener 监听器中 onDestinationChanged 函数触发")
        }
    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    
    
        super.onCreateOptionsMenu(menu)
        // 加载资源文件中的菜单
        // 只加载菜单 , 此时不能跳转 , 需要重写 onOptionsItemSelected 方法才可以
        menuInflater.inflate(R.menu.my_menu, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
    
    
        // 重写了该方法 , 菜单选项才能生效
        // 优先使用 NavigationUI 进行导航 , 如果跳转失败 , 再使用传统的方式
        return NavigationUI.onNavDestinationSelected(item, navController)
                || super.onOptionsItemSelected(item)
    }

    // 默认状态下进入 FragmentB 后是无法返回的
    // 如果想要返回, 需要重写 onSupportNavigateUp 方法
    override fun onSupportNavigateUp(): Boolean {
    
    
        return NavigationUI.navigateUp(navController, appBarConfiguration)
                || super.onSupportNavigateUp()
    }
}

Supporting layout files:

<?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: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/fragmentContainerView"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:navGraph="@navigation/my_navigation_graph"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"  />

</androidx.constraintlayout.widget.ConstraintLayout>

Supporting menu layout file:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/fragmentB"
        android:icon="@mipmap/ic_launcher"
        android:title="跳转到 Fragment B"/>
</menu>

4. Execution results


Enter the interface, display the default FragmentA,

insert image description here

Click the menu bar in the upper right corner, and the "Jump to FragmentB" button will pop up, click this menu option, and jump to the page,

insert image description here

At this point, it jumps to the FragmentB page and triggers the listener,

insert image description here

Click the back button, return to the FragmentA interface, and trigger the listener again;

insert image description here

Guess you like

Origin blog.csdn.net/han1202012/article/details/132544587
Recommended