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 one
fragment
- And different
fragment
There are different ones in thereaction
operation - In addition to basic operations, but also in the corresponding
action
Add 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:
- through
HomeFragmentArgs.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