Android: Simple understanding and use of navigation in Android study notes

1. Basic concepts

1.1. Background

The UI architecture pattern of nesting multiple Fragments in a single Activity has been accepted by most Android engineers. It is necessary to manage the switching between Fragments through FragmentManager and FragmentTransaction.

In Android, the switching and management of pages includes the management of the application Appbar, the animation switching of Fragments, and the parameter transfer between Fragments. Moreover, the pure code method is not particularly friendly to use, and the Appbar is very confusing in the process of management and use. Therefore, Jetpack provides a component called Navigation, which is designed to facilitate developers to manage Fragment pages and Appbar.

Compared with the previous Fragment management that needs to rely on FragmentManagerand FragmentTransaction, using the Navigation component has the following advantages:

  • Visual page navigation diagram, which is convenient for us to sort out the relationship between pages
  • Complete navigation between pages through destination and action
  • Easy to add page switching animation
  • Type-safe parameter passing between pages
  • Unified management of menu/bottom navigation/drawer blue menu navigation through the Navigation UI class
  • Support deep link DeepLink

insert image description here

1.2. Meaning

  • Navigation is an important part of the Android Jetpack component package. With the help of Single Activity and multiple Fragment fragments, it optimizes the overhead of Android Activity startup and simplifies data communication between Activities.
  • Built-in support for jumping of ordinary Fragment, Activity and DialogFragment components, that is, all Dialog or PopupWindows are recommended to use DialogFragment to implement, which can cover all commonly used jumping scenarios and unify the management of the return stack.
  • In addition, state storage and recovery can be achieved based on Fragment.

Suppose you are a traditional Activity-based developer, and now you want to migrate to the Navigation navigation architecture, you must have the following questions:

  • All use Fragment? How to provide support for the startup type (singleTask, singleTop) of the original Activity jump?
  • How to pass data between Fragments?
  • How should onActivityForResult implement an "onFragmentResult" now?

2. Composition

2.1、Navigation graph

An XML resource containing all navigation related information

  • xml file, including all managed Fragments, start target, page change target, and return target.

2.2、NavHostFragment

A special kind of Fragment, a container for hosting navigation content

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

2.3、NavController

Manage the objects of application navigation, and realize operations such as jumping between Fragments

  • It is used to manage the navigation actions in NavHost, and is usually written in the click event to complete Fragment switching.
textView.setOnClickListener {

      findNavController().navigate(R.id.action_Fragment1_to_Fragment2)
  }

3. Basic use

3.1. Introducing dependencies

implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.0'

3.2. Create a navigation view

First make sure that AndroidStudio is above 3.3

  • 1. Right resclick, clickNew -> Android Resource Directory
  • Resource type2. Select from the drop-down list in the second line of the panel that appears Navigation, and then clickOK
  • 3. There will be an additional navigationresource directory under the res directory, right-click on the directory, click New -> Navigation Resource File, enter the name of the resource file to be created, name it here nav_graph, click ok, and one nav_graph.xmlwill be created.
    insert image description here

3.3. Configure graph: add fragment

The newly created nav_graph.xml switches to the design mode, clicks the plus sign at 2, and selects Create new destination to quickly create a new Fragment. Here, the newly created, , and FragmentAthree FragmentBfragments FragmentCare
insert image description hereinsert image description herecreated, and the page can be configured manually The jump relationship between, click on a page, a small dot will appear on the right, 拖曳小圆点pointing to the page to jump to, here set the jump relationship to FragmentA -> FragmentB -> FragmentC.
insert image description hereSwitch to the Code column, you can see that the following code is generated

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

    <fragment
        android:id="@+id/fragmentA"
        android:name="com.example.testnavigation.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" >
        <action
            android:id="@+id/action_fragmentA_to_fragmentB2"
            app:destination="@id/fragmentB" />
    </fragment>
    <fragment
        android:id="@+id/fragmentB"
        android:name="com.example.testnavigation.FragmentB"
        android:label="fragment_b"
        tools:layout="@layout/fragment_b" >
        <action
            android:id="@+id/action_fragmentB_to_fragmentC2"
            app:destination="@id/fragmentC" />
    </fragment>
    <fragment
        android:id="@+id/fragmentC"
        android:name="com.example.testnavigation.FragmentC"
        android:label="fragment_c"
        tools:layout="@layout/fragment_c" />
</navigation>
  • navigationIt is the root tag, the first page started by default through startDestination configuration, here is FragmentA
  • fragmentA tag represents a fragment. In fact, not only fragments but also activities can be configured here, and even customized
  • actionThe label defines the behavior of the page jump, which is equivalent to each line in the above figure. The destination defines the target page of the jump, and can also define the animation when jumping, etc.
    • When the action_FragmentA_to_FragmentB2 is called, it will change from FragmentA -> FragmentB

3.4, add NavHostFragment

Configure NavHostFragment in the layout file of MainActivity

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

    <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:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>
  • android:nameSpecify NavHostFragment
  • app:navGraphSpecify the navigation view, that is, the built nav_graph.xml
  • app:defaultNavHost=trueIt means that the return key of the system can be intercepted. It can be understood that the function of the return key is implemented for the fragment by default. In this way, during the jump process of the fragment, when we press the return key, the fragment can return to the previous one just like the activity page up

Now that we run the program, we can run normally and see the page displayed by FragmentA. This is because NavHostFragment is configured in the layout file of MainActivity, and the navigation view is specified for NavHostFragment, and the default display is specified by startDestination in the navigation view. FragmentA.

3.5. Manage jumps between fragments through NavController

As mentioned above, the jump relationship between the three fragments is FragmentA -> FragmentB -> FragmentC, and FragmentA can already be displayed, so how to jump to FragmentB? This requires the use of NavController. Open the FragmentA class and give it to the
layout TextView defines a click event

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    tv.setOnClickListener {
        val navController = Navigation.findNavController(it)
        navController.navigate(R.id.action_fragmentA_to_fragmentB2)
    }
}

If it is found that the layout file cannot be imported automatically, the high probability is to add a plug-in to app.build‘kotlin-android-extensions’

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
}

It can be seen that it is very simple to manage the jump of fragments through navController. First, get the navController object, then call its navigate method, and pass in the id of the action defined in the previous nav_graph.

In the same way, set a click event for the TextView in FragmentB, so that it jumps to FragmentC when clicked

Run the program, FragmentA -> FragmentB -> FragmentC, press the back button at this time, it will also return one by one page, if the previous app:defaultNavHostsetting is set to false, after pressing the back button, you will find that you will return to the desktop directly.

3.5.1. Acquisition of NavController and its capabilities

In the above example, we can get the Fragment's subordinate NavController through the Fragment's extension method, and there are some overloaded methods:

// 根据 viewId 向上查找
NavController findNavController(Activity activity, int viewId)
// 根据 view 向上查找
NavController findNavController(View view)

In essence, findNavController is to find the NavController corresponding to the parent NavHostFragment closest to the specified view in the current view tree, and it is only for understanding at present.

Capabilities of NavController

For the application layer, we only deal with NavController in the entire Navigation framework, which provides common functions such as jumping, returning, and obtaining the return stack.
insert image description hereinsert image description here

4. Pass parameters when jumping

4.1. Pass parameters through the navigate method with bundle parameters

Parameters can be passed to the destination by specifying the bundle parameter, for example:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    tv.setOnClickListener {
        val navController = Navigation.findNavController(it)
        val bundle = Bundle()
        bundle.putString("key", "test")
        navController.navigate(R.id.action_fragmentA_to_fragmentB2, bundle)
    }
}

In the destination Fragment, the bundle can be obtained directly through the getArguments() method.

    super.onCreate(savedInstanceState)
    val value = arguments?.getString("key")
    ...
}

4.2. Through the safeArgs plug-in

Compared with the traditional way of passing parameters, afe args has the advantage of safe parameter types, and with the official support of Google, it is very convenient to pass parameters.

1. 根build.gradleAdd the plugin under the project

buildscript {
    ext.kotlin_version = "1.3.72"
    repositories {
        google()
        jcenter()
    }
    dependencies {
         classpath "com.android.tools.build:gradle:7.0.4"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
       classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0"
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

2, and then app的build.gradlereferenced in'androidx.navigation.safeargs.kotlin'

    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
    id 'androidx.navigation.safeargs.kotlin'
}

3. After adding the plug-in, return to nav_graph, switch to design mode, and add the parameters that need to be received to the target page

Here you need to pass parameters when FragmentA jumps to FragmentB, so set parameters for FragmentB, click FragmentB, click + on the right side of Arguments on the right panel, enter the key value of the parameter, specify the parameter type and default value, and you can quickly add parameters
insert image description here4. After adding, rebuildclick on the project, safeArgs will automatically generate some codes, /build/generated/source/navigation-argsyou can see in the directory that
insert image description heresafeArgs will generate corresponding classes according to the fragment tag in nav_graph,

  • The action tag will be “类名+Directions”named after,
  • The argument tag will be “类名+Args”named after .

After using safeArgs, pass parameters like this

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        tv.setOnClickListener {
            val navController = Navigation.findNavController(it)
            //通过safeArgs传递参数
            val navDestination = FragmentADirections.actionFragmentAToFragmentB2("test")
            navController.navigate(navDestination)
            
            // 普通方式传递参数
		   // val bundle = Bundle()
            // bundle.putString("key", "test")
            // navController.navigate(R.id.action_fragmentA_to_fragmentB2, bundle)
        }
    }

The receiving parameters are like this

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        arguments?.let {
            val value = FragmentBArgs.fromBundle(it).key
            .......
        }
        .......
    }

5. Animation

5.1. Action parameter setting animation

enterAnim: 跳转时的目标页面动画

exitAnim: 跳转时的原页面动画

popEnterAnim: 回退时的目标页面动画

popExitAnim:回退时的原页面动画

insert image description hereAdd anim
insert image description here
slide_in_right.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="500"
        android:fromXDelta="100%"
        android:fromYDelta="0%"
        android:toXDelta="0%"
        android:toYDelta="0%" />
</set>

slide_out_left.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="500"
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:toXDelta="-100%"
        android:toYDelta="0%" />
</set>

slide_in_left.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="500"
        android:fromXDelta="-100%"
        android:fromYDelta="0%"
        android:toXDelta="0%"
        android:toYDelta="0%" />
</set>

slide_out_right.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="500"
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:toXDelta="100%"
        android:toYDelta="0%" />
</set>

Add to action: You can use alpha, scale, rotate, translate these effects according to different needs

<action
    android:id="@+id/action_page1_to_action_page2"
    app:destination="@id/page2Fragment"
    app:enterAnim="@anim/slide_in_right"
    app:exitAnim="@anim/slide_out_left"
    app:popEnterAnim="@anim/slide_in_left"
    app:popExitAnim="@anim/slide_out_right" />

5.2. Shared elements

If two pages have similar elements, you can use this method to give the visual a sense of being carried over continuously.

Add the attribute transitionName to the elements shared by the two pages, and the value of the attribute must be the same.
fragment_one.xml

<ImageView
        android:id="@+id/catImageView"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:src="@mipmap/cat"
        android:transitionName="catImage" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="cat"
        android:transitionName="catText" />

fragment_two.xml

<ImageView
        android:id="@+id/catImageView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:src="@mipmap/cat"
        android:transitionName="catImage" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="cat"
        android:transitionName="catText" />

PageOneFragment.kt
Assign the transitionName of the xml element to NavController

val extras = FragmentNavigatorExtras(
            catImageView to "catImage",
            textView to "catText")
            
            catImageView.setOnClickListener {
    
    

            findNavController().navigate(
            R.id.action_page1_to_action_page2,
            null,
            null, 
            extras)
        }

PageTwoFragment.java

@Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setSharedElementEnterTransition( TransitionInflater.from(requireContext())
                .inflateTransition(R.transition.shared_image));
    }

shared_image.xml

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds/>
    <changeImageTransform/>
</transitionSet>

6. Frequently asked questions

1. How to provide support for the startup type of Fragment jump (singleTask, singleTop)?

Stack management: click destination, you can also see popUpTo, popUpToInclusive, launchSingleTop
insert image description here
1, in the right panel launchSingleTop: if the stack already contains the specified interface to jump to, then only one will be kept, if not specified, two interfaces with the same interface will appear in the stack Fragment data can be understood as a singleTop similar to activity, that is, the stack top reuse mode, but it is a little different, for example, it will FragmentA@1 -> FragmentA@2,FragmentA@1be destroyed, but if it is FragmentA@01>FragmentB@02>FragmentA@03, FragmentA@1it will not be destroyed.

2. popUpTo(tag): Indicates jumping to a certain tag and popping the elements above the tag out of the stack.

3. popUpToInclusive: If it is true, the tag will pop up, if it is false, it will not

Example: FragmentA -> FragmentB -> FragmentC -> FragmentA

FragmentC -> FragmentAIf the action is set to popUpTo=FragmentA ,popUpToInclusive=false, then the elements in the stack will change to
insert image description herethe last, and you will find that you need to press the return key twice to return to the desktop

When setting popUpToInclusive=true, the elements in the stack change so that
insert image description hereyou only need to press the return key once to return to the desktop. From this, you can understand the meaning of popUpTo and popUpToInclusive.

2. How to communicate between Fragments?

The communication in Fragment can also be divided into two scenarios. Suppose there are two Fragments in the return stack, namely A and B.

  • If A and B are in the same level submap, the interaction can be completed by creating ViewModels at the navigation map level at both ends.

For example, the current return stack is

NavGraphA -> NavDestinationB -> NavDestinationC -> NavDestinationD

1. If you want to realize the communication between C and D, you need to use Node B to create a ViewModel.

val vm by navGraphViewModels<TitleVm>(R.id.nav_destination_b)

R.id.home is the nearest public parent Graph of the two, and the communication between the two is valid until the parent Graph is destroyed.

2. If A and B are not in the same-level subgraph, they can use the closest public parent Graph to complete the communication.

For example, the current return stack is

NavGraphA -> NavDestinationB -> NavGraphC -> NavDestinationD

If you want to realize the communication between B and D, you need to use A node to create ViewModel.

val vm by navGraphViewModels<TitleVm>(R.id.home)

3. Redrawing of navigation fragment

3.1, Fragment life cycle

The Fragment life cycle given by the official before the Navigation appears is as follows: ( 注意onDestroyView之处)
insert image description hereAfter the LIfecycle, Navigation and other components appear, the official Fragment life cycle diagram is as follows: (PS: Fragment Lifecycle && View Lifecycle)
insert image description hereFragment under the Navigation framework The life cycle is divided into Fragment Lifecycleand View Lifecycle, View Lifecycle is taken out separately, the reason is that Navigationthe non-stack top under the framework Fragmentwill be destroyed View, that is A跳转到B页面: A会执行onDestroyView销毁其 View(everything related to View, such as: Databinding, RecyclerView will be destroyed), But Fragment本身会存在(the member variables of Fragment itself will not be destroyed)

The correct state flow under the Navigation framework should be similar to this:

insert image description hereA opens B through action, A goes onResumeto onDestroyView, B goes from onAttachexecution to onResume, when B returns to A through the system return key, A starts from the above figure onCreateView流转到onResume, during this process A's View undergoes destruction and reconstruction, View (binding instance) The object instance is different, but the instance of Fragment A is always the same.

In such a scenario, suppose A has a network news list RecyclerView, and RecyclerView is destroyed and rebuilt along with View. How to save the data in it to avoid refreshing the data every time you return to A (resulting in: last browsing data, location loss, additional network resource consumption), so the data items of Adapter in RecyclerView are very critical!

Common storage methods are:

  • 1. Passed Fragmentmember variables
  • 2. ViewModel. In ViewModel's ViewModelScope, request network data through coroutines, save it in ViewModel (ViewModel life cycle runs through Fragment), save data through LiveData and ordinary variables, and restore data onViewCreatedlater

reference

1. Android navigation series - getting started
2. Android navigation series - advanced
3. Getting started with Navigation components
4. Android official architecture component Navigation: Fragment management framework that doesn't work
5. Navigation-02-Fragment life cycle
6. Fragment reconstruction phenomenon

Guess you like

Origin blog.csdn.net/JMW1407/article/details/125714708