This is the 28th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

preface

The Room for Jetpack was covered in the previous article. In this article Navigation will be enabled for a preliminary explanation!

1. Know Navigation

1.1 Birth of Navigation

The UI architecture of activities nested with multiple fragments has become very common, but managing fragments has always been a hassle. We need to manage switching between fragments through FragmentManager and FragmentTransaction.

Switching pages usually involves managing the App Bar, animating the switch between fragments, and passing parameters between fragments.

The pure code approach is not particularly user-friendly, and Fragments and App Bars are confusing to manage and use.

For this purpose, Jetpack provides a Navigation component designed to make it easy to manage pages and App Bar

1.2 Main elements of Navigation

  • Navigation Graph, a new XML resource file, contains all of your application’s pages and the relationships between them.
  • NavHostFragment, a special Fragment, can be regarded as a container for other fragments. The Fragment in the Navigation Graph is formally displayed by NavHostFragment.
  • NavController, used in code to perform specific page switches in the Navigation Graph.
  • The relationship between the three of them
    • When you want to switch fragments, use the NavController object, tell it which Fragment you want to go to in the Navigation Graph, and the NavController will display the Fragment you want to go to in the NavHostFragment.

Concept said a lot of, start actual combat!

2, Navigation actual combat

2.1 Preparations

The build.gradle corresponding to the project root directory needs to be imported

classpath 'androidx. Navigation: navigation - safe - the args - gradle - plugin: 2.3.0 - alpha06'
Copy the code

This corresponds to build.gradle in the App Module

plugins {
    id 'com.android.application'
    id 'kotlin-android'
/ / id 'androidx. Navigation. Safeargs' / / this is a Java introduction, the following is the kotlin
    id 'androidx.navigation.safeargs.kotlin'}... Slightly dependencies {... Slightly implementation'androidx. Navigation: navigation - fragments - KTX: 2.3.5'
    implementation 'androidx. Navigation: navigation - UI - KTX: 2.3.5'. Slightly}Copy the code

All right, we’re ready. Let’s get started!

2.2 Actual Combat

2.2.1 Layout and Relationship building

As is shown in

  • First create the corresponding Fragment and the corresponding layout, and associate them with each other
  • Create the corresponding Navigation resource file (my_nav_graph.xml) in the RES resource directory.

As is shown in

  • Then annotate 1 to add the corresponding Fragment, and then add the corresponding relationship to the corresponding Fragment
  • My relationship is: homeFragment fragmentable detailFragment; The detailFragment can also jump into the homeFragment

Take a look at the current XML code


      
<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_nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.dongnaoedu.navigation.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/action_homeFragment_to_detailFragment"
            app:destination="@id/detailFragment"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim" />

    </fragment>
    <fragment
        android:id="@+id/detailFragment"
        android:name="com.dongnaoedu.navigation.DetailFragment"
        android:label="fragment_detail"
        tools:layout="@layout/fragment_detail" >
        <action
            android:id="@+id/action_detailFragment_to_homeFragment"
            app:destination="@id/homeFragment" />
    </fragment>
</navigation>
Copy the code

As you can see from this code:

  • The outermost is the navigation tag, followed by a different onefragment
  • And differentfragmentThere are different ones in thereactionoperation
  • In addition to basic operations, but also in the correspondingactionAdd entry and exit animation in

2.2.2 Implementation of business logic

HomeFragment.kt

class HomeFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup? , savedInstanceState:Bundle?).: View? {
        return inflater.inflate(R.layout.fragment_home, container, false)}override fun onActivityCreated(savedInstanceState: Bundle?). {
        super.onActivityCreated(savedInstanceState)
        varbutton: Button? = view? .findViewById(R.id.button) button? .setOnClickListener {val navController = Navigation.findNavController(it)
            //id corresponds to the action ID of the Fragment in my_nav_graph. XML
            navController.navigate(R.id.action_homeFragment_to_detailFragment)
        }
    }

}
Copy the code

DetailFragment.kt

class DetailFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup? , savedInstanceState:Bundle?).: View? {
        return inflater.inflate(R.layout.fragment_detail, container, false)}override fun onActivityCreated(savedInstanceState: Bundle?). {
        super.onActivityCreated(savedInstanceState)
        valbutton: Button? = view? .findViewById(R.id.button2) button? .setOnClickListener {val navController = Navigation.findNavController(it)
            //id corresponds to the action ID of the Fragment in my_nav_graph. XML
            navController.navigate(R.id.action_detailFragment_to_homeFragment)
        }
    }
}
Copy the code

MainActivity.kt

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

		// Modify the ActionBar header text dynamically when switching the corresponding Fragment.
		// To make the operation obvious
        val navController = Navigation.findNavController(this, R.id.fragment)
        NavigationUI.setupActionBarWithNavController(this, navController)
    }
}
Copy the code

Let’s see how it works

We can see that the Fragment is already in place! But click on the top of the return can not return! How do you solve it?

Enter the MainActivity. Kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
		// Modify the ActionBar header text dynamically when switching the corresponding Fragment.
		// To make the operation obvious
        val navController = Navigation.findNavController(this, R.id.fragment)
        NavigationUI.setupActionBarWithNavController(this, navController)
    }

    override fun onSupportNavigateUp(a): Boolean {
        val navController = Navigation.findNavController(this, R.id.fragment)
        return navController.navigateUp()
    }
}
Copy the code

We see that the onSupportNavigateUp method has been rewritten to implement the corresponding logic (fixed code).

Let’s see how it works

As you can see from this operation, clicking on the above to return has returned successfully.

Now that Fragment jumping is implemented, how to pass values?

2.2.3 Fragment Data Transmission

Let’s look at the previous approach:

Corresponding HomeFragment

class HomeFragment : Fragment() {... slightlyoverride fun onActivityCreated(savedInstanceState: Bundle?). {
        super.onActivityCreated(savedInstanceState)
        varbutton: Button? = view? .findViewById(R.id.button) button? .setOnClickListener {var args = Bundle()
            args.putString("userName"."hqk")
            val navController = Navigation.findNavController(it)
            navController.navigate(R.id.action_homeFragment_to_detailFragment, args)
        }
    }
}
Copy the code

Corresponding DetailFragment

class DetailFragment : Fragment() {... slightlyoverride fun onActivityCreated(savedInstanceState: Bundle?). {
        super.onActivityCreated(savedInstanceState)

        var args = arguments
        varuserName: String? = args? .getString("userName")
// args? .getInt("userName")
        Log.d("hqk"."userName is $userName")
        
        valbutton: Button? = view? .findViewById(R.id.button2) button? .setOnClickListener {val navController = Navigation.findNavController(it)
            navController.navigate(R.id.action_detailFragment_to_homeFragment)
        }
    }
}
Copy the code

This is the way we’re more familiar with it, but I won’t show you how it works

  • When you evaluate bundles, you often copy and paste the key from the previous page and compare the data types.
  • Because when the bundle is evaluated, even if the key is passed incorrectly, or the corresponding variable type is chosen incorrectly, there will be no error message in the code
  • It is only when the corresponding logic is run that the corresponding value is known to have a problem

So is there a novel approach? If you ask, there must be!

Proceed to my_nav_graph.xml


      
<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_nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.hqk.navigation.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/action_homeFragment_to_detailFragment"
            app:destination="@id/detailFragment"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim"/>

        <argument
            android:name="user_name"
            app:argType="string"
            android:defaultValue="unknown"/>
        <argument
            android:name="age"
            app:argType="integer"
            android:defaultValue="0"/>
    </fragment>
    <fragment
        android:id="@+id/detailFragment"
        android:name="com.hqk.navigation.DetailFragment"
        android:label="fragment_detail"
        tools:layout="@layout/fragment_detail" >
        <action
            android:id="@+id/action_detailFragment_to_homeFragment"
            app:destination="@id/homeFragment" />
    </fragment>
</navigation>
Copy the code

Fragment fragment fragment fragment fragment fragment fragment fragment fragment fragment fragment fragment fragment

So how do you use it?

Enter the HomeFragment

class HomeFragment : Fragment() {... slightlyoverride fun onActivityCreated(savedInstanceState: Bundle?). {
        super.onActivityCreated(savedInstanceState)
        varbutton: Button? = view? .findViewById(R.id.button) button? .setOnClickListener {// var args = Bundle()
// args.putString("userName","hqk")
            
            var args = HomeFragmentArgs(userName = "hqk".18).toBundle()

            val navController = Navigation.findNavController(it)
            navController.navigate(R.id.action_homeFragment_to_detailFragment, args)
        }
    }

}
Copy the code

We see HomeFragmentArgs being used to pass the argument, but what about the receiver?

DetailFragment.kt

class DetailFragment : Fragment() {... slightlyoverride fun onActivityCreated(savedInstanceState: Bundle?). {
        super.onActivityCreated(savedInstanceState)

// var args = arguments
// var userName: String? = args? .getString("userName")
// args? .getInt("userName")
// Log.d("hqk", "userName is $userName")

        val args = HomeFragmentArgs.fromBundle(requireArguments())
        val userName = args.userName
        val age = args.age
        Log.d("hqk"."$userName.$age")
        
        valbutton: Button? = view? .findViewById(R.id.button2) button? .setOnClickListener {val navController = Navigation.findNavController(it)
            navController.navigate(R.id.action_detailFragment_to_homeFragment)
        }
    }
}
Copy the code

Here we see:

  • throughHomeFragmentArgs.fromBundle(requireArguments())The bundle information is retrieved from the bundle, and the value is directly accessed through the corresponding property!
  • Great run-time error avoidance! You don’t switch back and forth between the variable name and the variable type

Running effect I will not stick this, the reader can try

conclusion

This is the end of Navigation. Navigation is more than that, of course, so I’ll cover NavigationUI and DeepLink in the next article