preface
Navigation is one of the Android Jetpack components that makes single-activity apps the preferred architecture. In-app Fragment page jumps are handled by Navigation, eliminating the need to deal with the complexity of FragmentTransaction and associated transitions.
The specific use
Add dependencies to gradle.build of app:
def nav_version = "2.1.0."implementation "androidx.navigation:navigdef nav_version = "2.1.0" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"Copy the code
First we define three fragments: Fragment1, Fragment2, and Fragment3. Implementation logic: Click Fragment1 to jump to Fragment2, click Fragment2 to jump to Fragment3, click Fragment3 to Jump to Fragment1 and click the back button to return to Fragment1.
Navigation: The root of the navigation view XML. Where the jump logic of the fragment is defined.
The first step is to create a navigation folder in the RES resource directory and right-click to create a navigation resource file named nav_graph_main.xml.
The bottom left foot of the file is split into two tabs: Design and Text. The Design view is visual and you can select the fragment directly. The Text view is our hand-written configuration.
Let’s take a look at the nav_graph_main.xml file defined:
<? 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"
app:startDestination="@id/fragment1">
<fragment
android:id="@+id/fragment1"
android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment1"
android:label="Fragment1"
tools:layout="@layout/fragment1_layout">
<action
android:id="@+id/fragment1_action"
app:destination="@+id/fragment2" />
</fragment>
<fragment
android:id="@+id/fragment2"
android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment2"
android:label="Fragment2"
tools:layout="@layout/fragment2_layout">
<action
android:id="@+id/fragment2_action"
app:destination="@+id/fragment3" />
</fragment>
<fragment
android:id="@+id/fragment3"
android:name="com.jetpack.jetpackdemo.navigation.fragment.Fragment3"
android:label="Fragment3"
tools:layout="@layout/fragment3_layout">
<action
android:id="@+id/fragment3_action"
app:popUpTo="@id/fragment1" />
</fragment>
</navigation>Copy the code
Navigation has a startDestination field in the root node, which indicates which page is displayed by default. Use the Fragment tag to define the relevant page to route. Id Uniquely identifies the fragment. Name indicates the package name and must be correct. Layout is a fragment layout file. After configuration, you can view it in the Design view.
Fragment has child actions configured. Action indicates the specific behavior to be routed. The ID is also the unique identifier. Destination indicates the destination, that is, to route to a specific page. PopUpTo indicates that a page is displayed. Action has other properties such as configuration animation, see Demo for more details.
NavHostFragment is a display container for navigation views.
<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/nav_graph_main" />Copy the code
Name is a fixed notation and must be specified as
androidx.navigation.fragment.NavHostFragmentCopy the code
The defaultNavHost field indicates whether to intercept the return key operation.
If true, override the onSupportNavigateUp method in the required Activity.
By default, the return key does not roll back the fragment page.
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp()
}Copy the code
The navGraph field is the navigation view configured for us.
NavController
FindNavController is used to obtain the NavController, and navigate between pages through the Controller’s navigate or navigateUp.
The logic of clicking buttons on three pages is to challenge the corresponding page:
mBtn.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.fragment1_action)
}Copy the code
Tells Navigation the logic of the jump by specifying the id of the action. The same goes for other pages.
End result:
Let’s summarize the relationship between Navigation, NavHostFragment and NavController.
Navigation means planning a lot of routes that need to be displayed in NavHostFragment. After showing so many routes, the NavController decides which route to take, like a steering wheel.
Passing parameters
In the previous section we explained navigation, which also has a child tag: argument. Is used to define parameters. For example, we add the argument tag to the fragment2 tag like this:
<argument
android:name="name"
android:defaultValue="Navigation navigation"
app:argType="string"
app:nullable="false" />Copy the code
You can then carry parameters when Fragment1 jumps to Fragment2. Name indicates the parameter name. DefaultValue is the defaultValue. ArgType is the type of the argument. Nullable: indicates whether it can be null.
There are two ways to pass parameters between fragments:
- The traditional Bundle approach
- SafeArgs via Google
The traditional Bundle approach
Set and get parameters through the Bundle.
Set this in fragment1:
Mbtn.setonclicklistener {// If you want to use the default argument in XML, just pass in new Bundle(). Val args = Bundle() args.putString()"name"."Passing parameters through the bundle")
Navigation.findNavController(it).navigate(R.id.fragment1_action, args)
}Copy the code
Get the parameter in fragment2:
val args = arguments val name = args? .getString("name")
mTvName.text = nameCopy the code
This allows the parameters to be passed. Is there anything wrong with this approach?
Parameter name “name” is manually filled in three places. This can easily lead to spelling mistakes and missing corrections. Very unfriendly. So Google gave us a plugin: safeArgs. Now let’s look at the specific use.
safeArgs
First you need to configure it by adding the classpath configuration to your project’s build.gradle:
dependencies {
classpath 'android. Arch. Navigation: navigation - safe - the args - gradle - plugin: 1.0.0'
}Copy the code
Add the Apply Plugin to build.gradle of app.
apply plugin: 'androidx.navigation.safeargs'Copy the code
After the project is rebuilt, it will know that the file with the suffix Directions is generated for the fragment. And automatically generates a file with the suffix Args for the fragment with the argument tag in navigation.
Set the parameters from the file with the suffix Directions. Obtain parameters from files with the suffix Args.
Set in fragment1:
mBtn.setOnClickListener {
val args = Fragment1Directions.fragment1Action().setName("Parameter passing via safeArgs")
Navigation.findNavController(it).navigate(R.id.fragment1_action, args.arguments)
}Copy the code
Obtain from fragment2:
val name = Fragment2Args.fromBundle(arguments!!) .name mTvName.text = nameCopy the code
This completes the passing of parameters between fragments. The logic of manually setting parameters is completely avoided. Operate on parameters directly through setters and getters.
conclusion
Generally speaking, Navigation is not complicated to use and it makes it possible to use a single Activity architecture without worrying about the specific fragment jump logic. But there are also problems, as we know from source code analysis
In the onCreateView of NavHostFragment, FrameLayout is created, so the real container is FrameLayout. Instead of show and hide, the replace API is used internally to create FragmentNavigator. This causes the fragment to be reexecuted every time it lives. So it should work better with the ViewModel.