preface

Some time ago, I encountered some small problems with Fragment management when I was doing project development. I always felt that the packaged Fragment manager was not elegant at the present stage. This led me to decide to learn Jetpack’s Navigation library, which was launched a long time ago to manage fragments more elegantly. When learning new knowledge, I prefer to write down my knowledge points and difficulties on paper. But sometimes because of the tight time, things written down on paper are often not so detailed and specific. So I decided to make a summary of the knowledge by writing a blog in the future, and ALSO hope to help you reading this blog. Let’s improve the Android knowledge system together!

Conditions of use

If you want to use the Navigation component in Android Studio, you must use Android Studio 3.3 or higher.

Add the dependent

To add Navigation support to your project, add the following dependencies to your application’s build.gradle file:

dependencies {
    //...
    implementation "Androidx. Navigation: navigation - fragments - KTX: 2.2.1." "
    implementation "Androidx. Navigation: navigation - UI - KTX: 2.2.1." "
}
Copy the code

The specific process of using Navigation

Project presentations

Create three new FramgNets

Before configuring Navigation, we create three FramgNets for testing. The code looks like this:

// first Fragment class Page1Fragment:Fragment() { override fun onCreateView(inflater: LayoutInflater,container: ViewGroup? ,savedInstanceState: Bundle?) : View? {return inflater.inflate(R.layout.fragment_page_1, container, false}} // Second Framgnet class Page2Fragment:Fragment() { override fun onCreateView(inflater: LayoutInflater,container: ViewGroup? ,savedInstanceState: Bundle?) : View? {return inflater.inflate(R.layout.fragment_page_2, container, false}} // check whether Page3Fragment is displayed.Fragment() { override fun onCreateView(inflater: LayoutInflater,container: ViewGroup? ,savedInstanceState: Bundle?) : View? {return inflater.inflate(R.layout.fragment_page_3, container, false)}}Copy the code

Configuration navigation

  • We need to create a new Navigaton folder under the RES folder.
  • Create a Navigation resource file in the Navigaton folder.
  • We’ll call it mobile_navigaion.xml.

As shown below:

You can either nest another
tag inside the
tag, or create a new Navigation XML file and import it via

.

Add the three fragments we just created to mobile_navigation.xml.


















The code looks like this:
















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

    <fragment
        android:id="@+id/fragment_page_1_id"
        android:name="com.johnlion.navigation.Page1Fragment"
        android:label="fragment_page_1_label"
        tools:layout="@layout/fragment_page_1" />

    <fragment
        android:id="@+id/fragment_page_2_id"
        android:name="com.johnlion.navigation.Page2Fragment"
        android:label="fragment_page_2_label"
        tools:layout="@layout/fragment_page_2" />

    <fragment
        android:id="@+id/fragment_page_3_id"
        android:name="com.johnlion.navigation.Page3Fragment"
        android:label="fragment_page_1_label"
        tools:layout="@layout/fragment_page_3" />

</navigation>
Copy the code

In the mobile_navigation. XML Fragment we just added, the
tag will be red in the Android Studio compiler. This is because we didn’t add NavHostFragment to the Activity layout file properly.

We open the Activity layout file and add a NavHostFragment to the layout file. The code looks like this:

<? 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/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        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/mobile_navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code

Please note the following points:

The compiler does not provide intelligent prompts for NavHostFragment file path, defaultNavHost, and navGraph properties when adding NavHostFragment. Please do not panic, do not doubt whether there are these attributes or values, firmly and accurately knock it out or copy and paste success!

  • android:nameProperty contains the class name of the NavHost implementation.
  • app:navGraphProperty to associate a NavHostFragment with a navigation diagram. The navigation diagram specifies all the destinations the user can navigate to in this NavHostFragment.
  • app:defaultNavHost="true"Property to ensure that your NavHostFragment intercepts the system return button. Note that there can only be one default NavHost. If there are multiple hosts in the same layout (for example, a two-pane layout), be sure to specify only one default NavHost.

NavHostFragment is simply a navigation interface container that displays a series of fragments in navigation.

After NavHostFragment is added to the Activity layout file, the red warning of the navigation TAB in mobile_navigation. XML is replaced by a yellow alert. This is because we did not add a “start destination” in the navigation tag! So we open the first page of the app. In the
TAB add:

app:startDestination="@id/fragment_page_1_id"

Now in Navigation Edit, a small house is added to the top of the Fragment as the starting destination. As shown below:

So the Page1Fragment we created will be the first screen we display when we open the app.

After the “start destination” is configured, we need to configure the “destination” for each fragment. There are two ways to configure the “destination” : one is to drag the visual Fragment arrow in Navigation Edit to point to its “destination”; The second is to add the

tag to each Fragment and configure its “destination” directly in the XML. The code looks like this:

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

    <fragment
        android:id="@+id/fragment_page_1_id"
        android:name="com.johnlion.navigation.Page1Fragment"
        android:label="fragment_page_1_label"
        tools:layout="@layout/fragment_page_1">
        <action
            android:id="@+id/action_page_1_to_page_2"
            app:destination="@id/fragment_page_2_id" />
    </fragment>

    <fragment
        android:id="@+id/fragment_page_2_id"
        android:name="com.johnlion.navigation.Page2Fragment"
        android:label="fragment_page_2_label"
        tools:layout="@layout/fragment_page_2">
        <action
            android:id="@+id/action_page_2_to_page_3"
            app:destination="@id/fragment_page_3_id" />
        <action
            android:id="@+id/action_page_2_to_page_1"
            app:popUpTo="@id/fragment_page_1_id" />
    </fragment>

    <fragment
        android:id="@+id/fragment_page_3_id"
        android:name="com.johnlion.navigation.Page3Fragment"
        android:label="fragment_page_1_label"
        tools:layout="@layout/fragment_page_3">
        <action
            android:id="@+id/action_page_3_to_page_2"
            app:popUpTo="@id/fragment_page_2_id" />
    </fragment>

</navigation>
Copy the code

The application navigation diagram is as follows:

Realize the jump

Next we configure the corresponding jump event for each Fragment. The code looks like this:

// first Fragment class Page1Fragment:Fragment() { override fun onCreateView(inflater: LayoutInflater,container: ViewGroup? ,savedInstanceState: Bundle?) : View? {return inflater.inflate(R.layout.fragment_page_1, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) btn_page1.setOnClickListener { Navigation. FindNavController (it). Navigate (da ction_page_1_to_page_2 R.i)}}} / / the second Fragment class Page2Fragment:Fragment() { override fun onCreateView(inflater: LayoutInflater,container: ViewGroup? ,savedInstanceState: Bundle?) : View? {return inflater.inflate(R.layout.fragment_page_2, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) btn_page2_1.setOnClickListener { Navigation.findNavController(it).navigateUp() } btn_page2_2.setOnClickListener { Navigation.findNavController(it).navigate(R.id.action_page_2_to_page_3) } } } Class Page3Fragment:Fragment() { override fun onCreateView(inflater: LayoutInflater,container: ViewGroup? ,savedInstanceState: Bundle?) : View? {return inflater.inflate(R.layout.fragment_page_3, container, false)
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        btn_page3.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_page_3_to_page_2)
        }
    }
}
Copy the code

The API used for jumping between fragments is:

  • Navigation. FindNavController (view). Navigate (actionID).
  • Navigation. FindNavController (view). NavigateUp ().

Use the Bundle to pass parameters

The code looks like this:

btn_bundle.setOnClickListener {
            val bundle = Bundle()
            bundle.putString("key"."value")
            Navigation.findNavController(it).navigate(R.id.action_page_1_to_page_2, bundle)
        }
Copy the code

Insert the key: value to be passed into the Bundle by creating a Bundle() object, then our navigate(…) Method, so that we can pass parameters through the bundle.

Interface switching animation

We can animate transitions between destinations to make switching from Fragment to Fragment easier. So let’s try to animate right in and left out.

Create an ANim folder in the res directory to store the XML file that implements the animation. Then create two animation resource files named slide_in_right. XML and slide_out_left. XML, as shown below:

//slide_in_right.xml <? xml version="1.0" encoding="utf-8"? > <set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="100%"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="0" />
</set> //slide_out_left <? xml version="1.0" encoding="utf-8"? > <set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="100%"/>
</set>
Copy the code

After the animation file is added, you can configure the animation transition between fragments in two ways. One is under the < Action > TAB in the navigation, and the other is through the navOptions method in the code. The animation configuration code is as follows:

<action Android :id= // We animate one Fragment to the second Fragment"@+id/action_page_1_to_page_2"
        app:destination="@id/fragment_page_2_id"
        app:enterAnim="@anim/slide_in_right"
        app:exitAnim="@anim/slide_out_left"Val option = navOptions {anim {enter = r.nim.slide_in_rightexit = R.anim.slide_out_left
            }
    }
    btn_page1.setOnClickListener {
        Navigation.findNavController(it).navigate(R.id.action_page_1_to_page_2, null, option)
    }
Copy the code

Navigate: navigate(…) The first argument is passed in the actionID, the second argument is passed in the Bundle object, and the third argument is passed in the navOptions, since we are not creating a bundle.

This completes the animation of our transition between destinations

At this point, by running the preview, you have basically the same effect as the example

Use Safe Args to deliver secure data

The Navigation component has a Gradle plug-in called Safe Args that generates simple Object and Builder classes for browsing and accessing any associated parameters in a type-safe manner. We strongly recommend that you use Safe Args for navigation and data delivery because it ensures type safety.

Configure the security plug-in first.

Gradle buildScript {repositories {//... google() } dependencies { //... def nav_version ="2.1.0."
            classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"}} // Build. Gradle at application or module level // apply plugin:'com.android.application'
    apply plugin: "androidx.navigation.safeargs.kotlin"
    android{
        //...
        kotlinOptions {
            jvmTarget = JavaVersion.VERSION_1_8
        }   
    }
Copy the code

When the security plug-in is complete, it automatically generates a class with Directions at the end, the name of which is “Directions” after the name of the source destination, and the method for the action of the original destination.

As described in the official documentation, the Navigation library supports the following parameter types:

First we need to add our custom parameters to mobile_navigation. XML. If we want to pass the parameters from Page1 to Page2, Add the argument tag under the fragment tag in Page2 and add the name, default value, and type. The code looks like this:

<fragment
        android:id="@+id/fragment_page_2_id"
        android:name="com.johnlion.navigation.Page2Fragment"
        android:label="fragment_page_2_label"
        tools:layout="@layout/fragment_page_2"> <! -... --> <argument android:name="myInteger"
            android:defaultValue="0"
            app:argType="integer" />
        <argument
            android:name="myString"
            android:defaultValue="value"
            app:argType="string" />
        <argument
            android:name="myBoolean"
            android:defaultValue="false"
            app:argType="boolean" />
    </fragment>
Copy the code

After adding, we must reBuild the project, so that the security plug-in will generate for us a class with “Args” at the end, which contains the method that we receive the parameter destination Page2 to get the parameter.

Page1FragemntDirections

class Page1FragmentDirections private constructor() {
    //...
    companion object {
        fun actionPage1ToPage2(
            myInteger: Int = 0,
            myString: String = "value",
            myBoolean: Boolean = false
        ): NavDirections = ActionPage1ToPage2(myInteger, myString, myBoolean)
    }
}
Copy the code

Let’s take a look at how to pass secure data in code. The code looks like this:

// class Page1Fragment:Fragment() {
    //...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        btn_page1.setOnClickListener {
            val action = Page1FragmentDirections.actionPage1ToPage2(1, "hello".true) Navigation. FindNavController (it). Navigate (action)}} / / Page2Fragment (receive safety data) class Page2Fragment:Fragment() { val args: Page2FragmentArgs by navArgs() override fun onViewCreated(view: View, savedInstanceState: Bundle?) {/ /... Log.d(log.d) = log.d (log.d)"data"."integer:" + args.myInteger)
        Log.d("data"."string:" + args.myString)
        Log.d("data"."boolean:" + args.myBoolean)
    }
}
Copy the code

If we are in actionPage1ToPage2(…) If nothing is added to the method, android:defaultValue is passed in the XML setting.

The argument passed from Page1 to Page2 must not be null, but if the argument type supports null values, you can use

Use android:defaultValue=”@null” and app:nullable=”true” to declare the defaultValue null.

In general, safe Args gives me the feeling that they are used to pass data around so as to avoid null-pointer exceptions when receiving data!

Update interface components using NavigationUI

The navigation architecture component contains the NavigationUI class. This class contains static methods for managing navigation using the top application bar, the drawer navigation bar, and the bottom navigation bar.

NavigationUI supports the following types of controls:

  • Toolbar
  • CollapsingToolbarLayout
  • ActionBar
  • DrawerLayout
  • BottomNavigationView

Let’s select BottomNavigationView with Navigation for an example:

  • Create a new menu folder under the res directory.
  • Create a Menu resource file in the Menu folder named menu.xml.
  • Add the following code to this file:

Note: the id in item must match the Fragment ID you want to display in mobile_navigation.xml

<? xml version="1.0" encoding="utf-8"? > <menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/fragment_page_1_id"
        android:icon="@drawable/message"
        android:title="Page1" />
    <item
        android:id="@+id/fragment_page_2_id"
        android:icon="@drawable/search"
        android:title="Page2" />
    <item
        android:id="@+id/fragment_page_3_id"
        android:icon="@drawable/setting"
        android:title="Page3" />
</menu>
Copy the code

We then add the BottomNavigationView control to the Activity layout file and associate the newly created Menu file with it. The code looks like this:

<? xml version="1.0" encoding="utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout ... > <fragment android:id="@+id/nav_host_fragment". /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:menu="@menu/menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code

Finally, we need to extract the NavController from the activity code and then pass it into BottomNavigationView setupWithNavController(…). So the first step is to find the NavHostFragment first, then retrieve the NavController, and finally pass it to setupWithNavController(…). Method that’s done binding Navigation to BottomNavigationView. The code looks like this:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main) val host: NavHostFragment = supportFragmentManager .findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ? :returnval navController = host.navController setupBottomNavMenu(navController) } private fun setupBottomNavMenu(navController:  NavController) { val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_navigation) bottomNav? .setupWithNavController(navController) } }Copy the code

The demo effect is as follows:

But! If you click on the BottomNavigationView item, then click on Page2 -> Page3, and then stay in Page3. If you click on the back button on the real phone, you will jump back to Page1, and then click again to exit the application. Try it a few more times and you’ll get back to Page1 if you hit the back button and you’re not stuck in Page1.

What we want is: just click on BottomNavigation to select the Fragment, return to the stack with only one Fragment, and then click the Back button to exit the application.

Locating problems:

  • Enter thebottomNav? .setupWithNavController(navController)In the method.
  • It turns out there’s only oneNavigationUI.setupWithNavController(this, navController)Method and enter the method.
  • There’s one right thereBottomNavigationViewImplemented listeningsetOnNavigationItemSelectedListenerOne is returned inonNavDestinationSelected(item, navController);This method is used toassociatedNavigation with BottomNavigationView MenuItem.
  • That’s where the problem comes fromsetPopUpTo(int destinationId, boolean inclusive), the source code is as follows:
 public static boolean onNavDestinationSelected(@NonNull MenuItem item,@NonNull NavController navController) {
        NavOptions.Builder builder = new NavOptions.Builder()
        //...
        if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
            builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
        }
        NavOptions options = builder.build();
        try {
            navController.navigate(item.getItemId(), null, options);
            return true;
        } catch (IllegalArgumentException e) {
            return false; }}Copy the code

Every time we click on the BottomNavigationView Item, we’re going to go setPopUpTo(…). The inclusive=”false” inclusive=”false” inclusive=”false” inclusive=”false” inclusive=”false” inclusive=”false” inclusive=”false” inclusive=”false” inclusive=”false” inclusive=”false” inclusive=”false” inclusive=”false”

That’s why: clicking the back button will return to Page1 first if you’re not stuck in Page1!

Solve a problem:

  • The question comes from a man calledonNavDestinationSelected(item, navController);In the method of.
  • The method only realized the navigation jump destination, and set the jump in and out animation and destination start mode.
  • This method is in BottomNavigationViewsetOnNavigationItemSelectedListenerListen in.
  • The listener is implemented onlyonNavDestinationSelected(item, navController)One way!

That we can try to write BottomNavigationView setOnNavigationItemSelectedListener monitoring, according to onNavDestinationSelected (…). Method inside the content, modify the effect we need not on the line!

The activity code looks like this:

class MainActivity : AppCompatActivity() {/ /... private fun setupBottomNavMenu(navController: NavController) { val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_navigation) bottomNav? . SetupWithNavController (navController) / / rewrite to monitor bottomNav setOnNavigationItemSelectedListener {item: MenuItem - > val options = NavOptions. Builder () / / removed from the back stack designated destination. SetPopUpTo (navController currentDestination!! .id,true)
                .setLaunchSingleTop(true)
                .build()
            try {
                //TODO provide proper API instead of using Exceptions as Control-Flow.
                navController.navigate(item.itemId, null, options)
                true
            } catch (e: IllegalArgumentException) {
                false}}}}Copy the code

The demo effect is as follows:

Done! The test results are exactly what we want!

Dynamically loaded Navigation

Remove app:navGraph=”@navigation/mobile_navigation” from the Activity layout file. The code looks like this:

<? xml version="1.0" encoding="utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout ... > <fragment android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
    />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code

Then in the Activity file, use the navController to feed the Navigation XML file to the Inflater and set it into the navController graph. The code looks like this:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main) val host: NavHostFragment = supportFragmentManager .findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ? :returnval navController = host.navController val navGraph: NavGraph = navController.navInflater.inflate(R.navigation.mobile_navigation) navController.graph = navGraph //... } / /... }Copy the code

Test again, dynamic loading successful!

Clear back stack

If we want to jump from Page2 to Page3, we should clear the back stack first, and then jump to Page3, then there should be only one instance of Page3 in the back stack. When we click back’, we will exit the application directly instead of putting Page2 back.

This is where I found a way to clear navigation back stack on StackOverflow.

The implementation code is as follows:

<? xml version="1.0" encoding="utf-8"? > <navigation xmlns:android="http://schemas.android.com/apk/res/android". > <! -... --> <fragment android:id="@+id/fragment_page_2_id". > <action android:id="@+id/action_page_2_to_page_3"
            app:destination="@id/fragment_page_3_id"
            app:launchSingleTop="true"
            app:popUpTo="@+id/mobile_navigation"
            app:popUpToInclusive="true" />
        <!--
            ...
        -->
    </fragment>
</navigation>
Copy the code

Add app:popUpToInclusive=”true”, app:popUpTo=”@+ ID /mobile_navigation”, app:popUpToInclusive=”true”, app:popUpToInclusive=”true”, app:popUpToInclusive=”true” You can clear the stack and jump.

We tried to implement this in code based on these attributes added to the XML. The code looks like this:

class Page2Fragment : Fragment() {/ /... override fun onViewCreated(view: View, savedInstanceState: Bundle?) {/ /... Btn_page2_.setonclicklistener {val navOption = navoptions.builder () // Set the following properties to the btn_page2_.setonClickListener {val navOption = navoptions.builder ()true)
                .setPopUpTo(R.id.mobile_navigation, true)
                .build()
            Navigation.findNavController(it).navigate(R.id.action_page_2_to_page_3, null, navOption)
        }
    }
}
Copy the code

Finally, implement a global stack clearing function in the Activity.

The code is very simple, take the NavController, Then the call navController. Navigate (XXX) method is called before navController. PopBackStack (R.i d.m obile_navigation, true) method can achieve the stack. The code looks like this:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main) val host: NavHostFragment = supportFragmentManager .findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ? :return
        val navController = host.navController
        setupBottomNavMenu(navController)
    }
    private fun clearStack(navController: NavController) {
        navController.popBackStack(R.id.mobile_navigation, true} / /... }Copy the code

Of course, “destination” between the jump, should be unified in the activity management, not detailed here.

As soon as you get your navController, you can call itnavController.navigate(xxx)Go to the Destination jump.

conclusion

Android JetPack’s Navigation architecture component, which serves as a framework for building in-app interfaces, focuses on making single-activity apps the preferred architecture. This control handles the complexity of FragmentTransaction and provides helper programs for associating navigation with appropriate UI widgets, such as the drawer navigation bar and bottom navigation.

Project link

Example: the android – navigation

Refer to the article

  • Navigation: a very awkward Fragment management framework
  • Android Navigation Architecture Component
  • The official documentation
  • Official Project Address
  • Official Project Tutorial