The author

Hello everyone, my name is Xiaoqi;

I graduated from The Software Engineering major of Central South University of Forestry and Technology in 2016. After graduation, I did Android development in the education industry. Later, I joined the Android team of 37 Mobile Games in October, 2016.

At present, I am mainly responsible for the development related to the domestic release of Android, while taking into account the development of several internal apps.

directory

  • Navigation — Introduction (this chapter)

  • Navigation – advanced

  • Navigation

preface

In daily development, an activity with multiple fragments is increasingly used. A typical example is the home page of an app, which is usually composed of an activity+ multiple sub-tabs. That for Fragment display, hide and so on we are usually managed by FragmentManager, but this way is easy to cause code bloat, difficult to maintain.

With Jetpack’s Navigation component, Navigation, you can easily manage switching between fragments, making development much easier.

Three elements

Navigation graph

An XML resource that contains all navigation-related information

NavHostFragment

A special Fragment used as a container for navigation content

NavController

Manage navigation objects and hop between fragments

The basic use

Introduction of depend on

implementation 'androidx. Navigation: navigation - fragments - KTX: 2.3.1'
implementation 'androidx. Navigation: navigation - UI - KTX: 2.3.1'
Copy the code

Creating a navigation view

First make sure AndroidStudio is above 3.3

1. Right-click Res and click New -> Android Resource Directory

2. In the second row of the panel that appears, select Navigation from the Resource Type drop-down list, and then click OK

3. Add a navigation directory to the res directory. Right-click the directory and click New -> Navigation Resource File. A nav_graph.xml is created.

Configuration graph

Go to design mode, click the plus sign at 2, and select Create New Destination to quickly Create a new Fragment. Here we create FragmentA, FragmentB, and FragmentC

Built, you can manually configure the jump between the pages, click a page, the right side will appear a dot, drag dot point to jump to the page, here set the jump relationship for FragmentA -> FragmentB -> FragmentC.

Switch to the Code bar and you can see the following Code generated


      
<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>
Copy the code
  • Navigation is the root TAB, and the first page to launch by default is configured with startDestination, where FragmentA is configured
  • The fragment tag represents a fragment. You can configure not only the fragment, you can also configure the activity, and you can even customize it (we’ll talk about that later).
  • The Action TAB defines the behavior of the page jump, equivalent to each line in the picture above, destination defines the target page of the jump, and can define the animation of the jump, and so on

Add NavHostFragment

Configure NavHostFragment in the layout file of MainActivity


      
<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>
Copy the code
  • The android: name specified NavHostFragment
  • App :navGraph specifies the navigation view, which is built as nav_graph.xml
  • App :defaultNavHost=true means that the system can intercept the return key of the fragment. If you press the return key in the fragment during the hop process, You can make the fragment go back to the previous page just like the activity

Now we can run the application and see the FragmentA display page. This is because the Layout file of the MainActivity has NavHostFragment configured and NavHostFragment is given a navigation view. The navigation view specifies the default display of FragmentA with startDestination.

Navigation between fragments is managed by NavController

If you want to jump to a FragmentA -> FragmentB -> FragmentC, if you want to jump to a FragmentB, if you want to jump to a NavController, if you want to jump to a FragmentB, if you want to jump to a NavController, you want to jump to a NavController

Open the FragmentA class and define a click event for the TextView in the layout

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)
    }
}
Copy the code

If you find that the layout file cannot be imported automatically, you should add the plugin ‘kotlin-Android-extensions’ to app.build.

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
Copy the code

Changed since AndroidStudio4.1

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

As you can see, navigating the fragment through navController is as simple as taking the navController object and calling its navigate method, passing in the ID of the action defined earlier in the NAV_graph.

In the same way, set a click event for the TextView in FragmentB to jump to FragmentC when clicked

If you set defaultNavHost to false, press the back key to return to the desktop. If you set defaultNavHost to false, press the back key to return to the desktop. Now you see what the app:defaultNavHost property means.

More usage

In addition to setting the target page, the action property can also set animation, page to page parameter passing, fragment back stack management, and so on

animation

  • EnterAnim: Target page animation when jumping

  • ExitAnim: Animation of the original page when jumping

  • PopEnterAnim: Target page animation during rollback

  • PopExitAnim: Animation of the original page on rollback

After configuring the animation, you will find that the action has four more animation-related properties

<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"
            app:enterAnim="@anim/enter_in"
            app:exitAnim="@anim/enter_out"
            app:popEnterAnim="@anim/exit_in"
            app:popExitAnim="@anim/exit_out" />
</fragment>
Copy the code

parameter

The above example shows jumping between fragments, but parameter passing is also possible.

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)
    }
}
Copy the code

It is recommended to use Google’s official safeArgs for parameter passing. Compared with the traditional method of passing parameters, safeArgs has the advantages of safe parameter types, and with the support of Google’s official, it is very convenient to pass parameter values.

Add the plugin under build.gradle at the root of your project

classpath "Androidx. Navigation: navigation - safe - the args - gradle - plugin: 2.3.1." "
Copy the code
buildscript {
    ext.kotlin_version = "1.3.72"
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "Com. Android. Tools. Build: gradle: 4.4.1." "
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "Androidx. Navigation: navigation - safe - the args - gradle - plugin: 2.3.1." "
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}
Copy the code

Then in the build of the app. Gradle referenced in the ‘androidx. Navigation. Safeargs. Kotlin’

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'androidx.navigation.safeargs.kotlin'
Copy the code

After AS4.1:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
    id 'androidx.navigation.safeargs.kotlin'
}
Copy the code

After adding the plugin, go back to nav_Graph, switch to Design mode, and add parameters to the target page that you want to receive. Here, you need to pass parameters when FragmentA jumps to FragmentB, so set parameters for FragmentB, click FragmentB, Click + on the right of Arguments to enter the key value of the parameter and specify the parameter type and default value to quickly add parameters

After adding, rebuild the project, safeArgs will automatically generate some code, in the/build/generated/source/navigation – args directory can be seen

SafeArgs generates classes based on the fragment tag in the Nav_graph. The action tag is named “class name +Directions” and the argument tag is named “class name +Args”.

With safeArgs, the passing parameter looks like this

    override fun onViewCreated(view: View, savedInstanceState: Bundle?). {
        super.onViewCreated(view, savedInstanceState)
        tv.setOnClickListener {
            val navController = Navigation.findNavController(it)
            // Pass parameters through safeArgs
            val navDestination = FragmentADirections.actionFragmentAToFragmentB2("test")
            navController.navigate(navDestination)
            
            // Pass parameters in normal mode
		   // val bundle = Bundle()
            // bundle.putString("key", "test")
            // navController.navigate(R.id.action_fragmentA_to_fragmentB2, bundle)}}Copy the code

The receive parameters look like this

    override fun onViewCreated(view: View, savedInstanceState: Bundle?). {
        super.onViewCreated(view, savedInstanceState) arguments? .let {valvalue = FragmentBArgs.fromBundle(it).key ....... }... }Copy the code

Stack management

Click Destination and you’ll also see popUpTo, popUpToInclusive, and launchSingleTop in the right pane

  • LaunchSingleTop: If the stack already contains the interface that you want to jump to, only one interface will be retained. If you do not specify the interface, the stack will display the same Fragment data for both interfaces. For example, FragmentA@1 -> FragmentA@2 and FragmentA@1 are destroyed, but FragmentA@01>FragmentB@02> 30000 is not destroyed.
  • PopUpTo (tag) : jumps to a tag and pushes the elements above the tag off the stack.
  • PopUpToInclusive: True means that tags will pop up, false does not

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

Set the action of FragmentC -> FragmentA to popUpTo=FragmentA, popUpToInclusive=false, then the stack element changes to

You’ll find that you need to press the back key twice to go back to the desktop

When popUpToInclusive=true, the stack element changes to

You only need to press the back key once to go back to the desktop, and you can see what popUpTo and popUpToInclusive mean.

deeplink

Deep links, that is, can directly jump to a page. Navigation creates deep links in both explicit and implicit ways

Create a new target page, FragmentDeepLink, that you want to open with a deep link,

Next, create a Deeplink for it

Nav_graph.xml generates the following code accordingly

<fragment
    android:id="@+id/fragmentDeepLink"
    android:name="com.example.testnavigation.FragmentDeepLink"
    android:label="fragment_deep_link"
    tools:layout="@layout/fragment_deep_link">
    <argument
        android:name="key"
        android:defaultValue="Test"
        app:argType="string" />
    <deepLink
        android:id="@+id/deepLink"
        app:uri="www.deeplink.com/{id}" />
</fragment>
Copy the code
  • Show deep links

    Use PendingIntent to navigate to a specific page, such as clicking a notification bar to quickly open the target page.

    tv_deeplink.setOnClickListener {
        // Display deep links
        val notificationManager = NotificationManagerCompat.from(requireContext())
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel =
                NotificationChannel(channelId, channelName, importance)
            channel.description = "deeplink"
            notificationManager.createNotificationChannel(channel)
        }
    
        val navController = Navigation.findNavController(it)
        val deepLinkBuilder = navController.createDeepLink()
        val bundle = Bundle()
        bundle.putString("key"."deeplink")
        val pendingIntent = deepLinkBuilder
             // Pass in the graph resource file
            .setGraph(R.navigation.nav_graph)
            // Pass in parameters
            .setArguments(bundle)
            // Pass in the target page to be opened with a deep link
            .setDestination(R.id.fragmentDeepLink)
            .createPendingIntent()
        val builder = NotificationCompat.Builder(requireContext(), channelId)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("Test deepLink")
            .setContentText("Ha ha ha.")
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        notificationManager.notify(1, builder.build())
    }
    Copy the code
  • Implicit deep linking

    Implicit links, which jump to a page via a URI when the user clicks on a link, have just been added for FragmentDeepLink in nav_graph.xml

    <deepLink app:uri="www.deeplink.com/{id}" />
    Copy the code

    The URI does not declare whether it is HTTP or HTTPS, so both will match. Inside the braces are the parameters passed.

    Androidmanifest.xml adds a
    property to the activity to which the FragmentDeepLink belongs, which is MainActivity

    <activity android:name=".MainActivity">.<nav-graph android:value="@navigation/nav_graph"/>
    </activity>
    Copy the code

    App – > build – > outputs – > apk – > debug – > app-debug.apk

<intent-filter>

    <action
        android:name="android.intent.action.VIEW" />

    <category
        android:name="android.intent.category.DEFAULT" />

    <category
        android:name="android.intent.category.BROWSABLE" />

    <data
        android:scheme="http" />

    <data
        android:scheme="https" />

    <data
        android:host="www.deeplink.com" />

    <data
        android:pathPrefix="/" />
</intent-filter>
Copy the code

The Navigation component replaces the
element with the generated < Intent-filter > element to match the deep link.

You can test the effect of implicit deep linking with ADB by opening the command line for input

adb shell am start -a android.intent.action.VIEW -d "http://www.deeplink.com/1"
Copy the code

In the system pop-up window, select your own application to open, you can jump to the target page.

conclusion

This is the introduction of navigation, mainly introduced the basic use of navigation, the next part will be from the source Angle, navigation is how to do the page jump.

conclusion

Students who have problems or need to communicate with each other in the process can scan the QR code and add friends, and then enter the group to communicate with each other about problems and technologies.