Android Jetpack - Navigation component: for application navigation

insert image description here

1. Introduction to the Navigation component

1.1 What is the Navigation component

  • The Navigation component is an Android Jetpack library that helps developers easily implement navigation in applications. The navigation component contains multiple classes and components, including navigation map, destination, navigation controller, etc., which can help us manage page navigation and task navigation in the application. By using the Navigation component, we can realize the navigation function of the application more conveniently, and at the same time improve the user experience of the application. In this article, we will introduce how to use the Navigation component to implement application navigation, and provide some examples and more extended functions.

1.2 Advantages of the Navigation component

  • The Navigation component makes it easy to navigate within an application, including transitions between pages and within an application.
  • Navigation components can improve the maintainability and extensibility of an application because they make the structure of the application clearer and make it easier to add new features and pages.
  • Navigation components provide a consistent user experience because they use standard navigation patterns and animations.
  • Navigation components help developers build applications faster because they provide many common navigation patterns and functionality that can be used directly or modified.
  • Navigation components can improve the testability of an application because they make navigation between pages and state transitions more explicit and controllable.

1.3 The Navigation component mainly consists of 3 parts:

  • NavHost: The container used to embed the navigation process, generally used FragmentContainerView.
  • NavController: NavHostThe controller responsible for handling navigation affairs internally, used to perform page jumps, manage return stacks, etc.
  • NavGraph: FragmentA resource file that describes the navigation relationship between pages, where transitions, animations, etc. between pages are defined. Usually placed res/navigation/in the directory.

In short, Navigationcomponents are NavHostused NavGraphto describe Fragmentnavigation paths and relationships, and NavControllerthen perform actual navigation work, which greatly simplifies the previous page jump logic and rollback stack management process.

2. Basic use of Navigation components

2.1 Add the navigation component to the project

  • Add the following dependencies to the project's build.gradle file:
dependencies {
    def nav_version = "2.5.3"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}
  • Add NavHostFragment in the layout file:
<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:navGraph="@navigation/nav_graph" />
  • Get the NavController object correctly:

When using NavController inside an Activity, it should be onCreate()fetched in:

val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
NavigationUI.setupActionBarWithNavController(this, navController)

And inside the Fragment, it should be fetched in onAttach()or onViewCreate():

val navHostFragment = parentFragment as NavHostFragment
val navController = navHostFragment.navController

NavHostIt is necessary to specify app:navGraphattributes to associate a navigation graph NavGraph, which determines Fragmentthe navigation relationship and jump path between pages.

NavControllerIs Navigationthe component 's control center, used to NavHostperform navigation operations within. The corresponding instance can be obtained by the attribute passed in Activityor .FragmentNavHostFragmentnavControllerNavController

Common navigation operations are:

  • Navigate to the target destination:navController.navigate(R.id.destination_id)
  • Fall back a destination: navController.navigateUp()ornavController.popBackStack()
  • Fallback to root destination:navController.popBackStack(R.id.root_destination, false)

NavControllerIt is also responsible for maintaining Fragmentthe rollback stack and popping the stack correctly when the return button is pressed, which greatly simplifies the Fragmentcomplexity of managing transactions before.
Through the cooperation of NavHostand NavController, Navigationthe component implements NavGraphthe navigation logic and page switching function declared in . This makes Fragmentnavigating between extremely simple and efficient. Developers only need to focus on the definition NavGraph, and call NavControllerthe navigation method in it to realize the page jump, and everything else is under Navigationthe control of the component.

2.2 Create a navigation graph

  • Create a navigation graph in an XML file:
<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:navGraph="@navigation/nav_graph" />
  • Create a folder under the res folder navigation, and then create a file under this folder nav_graph.xmlto define the structure and content of the navigation map:
<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/firstFragment">

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.smallmarker.jetpackpractice.navigation.fragment.FirstFragment"
        android:label="First"
        tools:layout="@layout/fragment_first">
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@+id/secondFragment" />
    </fragment>

    <fragment
        android:id="@+id/secondFragment"
        android:name="com.smallmarker.jetpackpractice.navigation.fragment.SecondFragment"
        android:label="Second"
        tools:layout="@layout/fragment_second" />

</navigation>

In a navigation graph, <fragment>elements are used to define destinations, android:idattributes are used to specify unique identifiers for destinations, android:nameattributes are used to specify class names for destinations, and android:labelattributes are used to specify label names for destinations as they appear in the application.

<action>The element is used to define the action, android:idthe attribute is used to specify the unique identifier of the action, and app:destinationthe attribute is used to specify the destination for the action to be executed.

3. Advanced use of Navigation components

3.1 Deep linking

In Android, a deep link is a link that takes the user directly to a specific destination within an app. With the Navigation component, you can create two different types of deep links: explicit deep links and implicit deep links.

  • Create an explicit deep link:

An explicit deep link is an instance of a deep link that uses a PendingIntentto take the user to a specific location within the app. For example, you can display explicit deep links in notifications or widgetin .

    val pendingIntent = NavDeepLinkBuilder(it)
        .setGraph(R.navigation.nav_deep_link)
        .setDestination(R.id.deepLinkFragment)
        .setArguments(
            Bundle().apply {
    
    
                putInt("id", 1)
            }
        )
        .setComponentName(DeepLinkActivity::class.java)
        .createPendingIntent()

    val notification = NotificationCompat.Builder(it, "my_channel")
        .setContentTitle("Title")
        .setContentText("测试深层链接")
        .setSmallIcon(R.mipmap.ic_launcher)
        .setContentIntent(pendingIntent)
        .build()

    NotificationManagerCompat.from(it).notify(Random.nextInt(10), notification)

This example uses NavDeepLinkBuilderthe class construction PendingIntent, add it to the notification and send it, click on the notification to jump to the specified page.

  • Create an implicit deep link:
<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_deep_link"
    app:startDestination="@id/deepLinkFragment">
  
    <fragment
        android:id="@+id/deepLinkFragment"
        android:name="com.smallmarker.jetpackpractice.navigation.fragment.DeepLinkFragment"
        android:label="DeepLink"
        tools:layout="@layout/fragment_deep_link">
  
        <deepLink app:uri="example://deepLink/{id}" />
  
    </fragment>

</navigation>

To enable implicit deep linking, you must also add content to your app's manifest.xmlfile . Add a <nav-graph>element to point to the existing navigation graph activity, as shown in the following example.

<activity
    android:name=".navigation.DeepLinkActivity"
    android:exported="true">
    <nav-graph android:value="@navigation/nav_deep_link" />
</activity>

In this example we define a deep link whose URI is "example://deepLink/{id}"where {itemId}is a parameter. When the user clicks this link in the browser or other applications, the Android system will automatically open our application, jump to the corresponding page, and pass the parameters to our application. We can get this parameter by passing in the target page arguments.

3.2 Shared element transitions

  • Shared element transitions can realize the animation effect of sharing the same element between different Activityor Fragmentbetween. For example, when clicking on a certain item on the list page to itementer the details page, the picture or text can be itemsmoothly transitioned between the two pages. Here is an example of a simple implementation:
  <!-- 在layout文件中定义共享元素的id -->
  <ImageView 
    android:id="@+id/image_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:transitionName="shared_element" />
  • Shared element transition to Fragment destination
val extras = FragmentNavigatorExtras(view1 to "shared_element")

view.findNavController().navigate(R.id.confirmationAction, null, null, extras)
  • Shared element transition to Activity destination
  val option = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, imageView, "shared_element")
  findNavController().navigate(R.id.shareElementDialog, null, null, ActivityNavigatorExtras(option))

Shared elements are provided programmatically rather than through navigation XML files. activityand fragmentDestination each have Navigator.Extrasa subclass of the interface that accepts additional options for navigation, including shared elements. You can pass these navigate()when Extras.

3.3 Dynamic construction of navigation graph - Dynamic construction of navigation graph can create different navigation graphs according to different conditions at runtime, such as displaying different navigation structures under different user login statuses and different permissions.

  • Here's a simple example of dynamically building a navigation graph:
  val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
  val navController = navHostFragment.navController
  val graph = navInflater.inflate(R.navigation.dynamic_nav_graph)
  
  if (isLoggedIn) {
    
    
      graph.startDestination = R.id.homeFragment
  } else {
    
    
      graph.startDestination = R.id.loginFragment
  }
  
  if (hasAdminPermissions) {
    
    
      val adminNode = NavGraphNavigator(navController.navigatorProvider.getNavigator(NavGraphNavigator::class.java))
          .createDestination()
      adminNode.id = R.id.adminFragment
      adminNode.setClassName("com.example.app.AdminFragment")
      graph.addDestination(adminNode)
      graph.addEdge(R.id.homeFragment, R.id.adminFragment)
  }
  
  navController.graph = graph

In the above example, we first get the current NavControllersum NavInflater, and then NavInflater.inflateload our dynamic navigation map through the method. Next, we set the starting destination of the navigation graph according to different conditions, and dynamically added a destination with administrator privileges, and added an edge to connect this destination and the home page. Finally, we set the built navigation map to NavController.

4. Best Practices for Navigation Components

4.1 Using <include>labels

  • Define separate for each module NavGraphIn large projects, it's better to define your own for each functional module NavGraph, then group each module's NavGraphwith a tag in the root::<include>NavGraph
  <navigation 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/root_navigation">
  
    <include 
        android:id="@+id/home_navigation"
        app:layout="@navigation/home_navigation" />
        
    <include 
        android:id="@+id/profile_navigation"
        app:layout="@navigation/profile_navigation" />  
  </navigation>

4.1 Using ViewModel and LiveData

  • Use LiveData object in ViewModel to handle navigation events:
class MainViewModel : ViewModel() {
    
    

    private val navigateTo = MutableLiveData<NavDirections>()

    fun getNavigateTo(): LiveData<NavDirections> {
    
    
        return navigateTo
    }

    fun setNavigateTo(directions: NavDirections) {
    
    
        navigateTo.value = directions
    }

}
  • Observe the LiveData object and handle navigation events in Fragment:
    class MainFragment : Fragment() {
    
    
        override fun onCreate(savedInstanceState: Bundle?) {
    
    
            super.onCreate(savedInstanceState)
            viewModel.getNavigateTo().observe(this) {
    
    
                Navigation.findNavController(requireView()).navigate(it)
            }
        }

        fun clickJump() {
    
    
            viewModel.setNavigateTo(MainFragmentDirections.actionMainToNavigationActivity())
        }

    }

Note that here we use Safe Argsto implement type-safe navigation and navigate between destinations. The official recommendation is to use Safe Args Gradleplugins. This plugin generates simple object and builder classes for type-safe navigation between destinations. We highly recommend using it when navigating and passing data between destinations Safe Args.
To Safe Argsadd this to your project, build.gradleinclude the following in your top-level fileclasspath:

buildscript {
    
    
    dependencies {
    
    
        def nav_version = "2.5.3"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

Then add the following line to your app or module's build.gradle file:

plugins {
    
    
    id("androidx.navigation.safeargs.kotlin")
}

V. Summary

5.1 Advantages and applicable scenarios of navigation components:

Advantage Applicable scene
1. Provide a consistent navigation experience Suitable for scenarios where multiple pages need to be introduced into the application
2. Simplify the navigation logic Suitable for scenarios that require complex navigation operations in the application
3. Customizable Appearance and Behavior Applicable to scenarios where the navigation bar needs to be customized according to application requirements
4. Support deep link Suitable for scenarios that need to support deep linking in the application

5.2 Best Practices for Navigation Components - When using navigation components, you should minimize manual Fragment transactions, but use the API provided by the navigation components to avoid unnecessary errors.

  • When designing a navigation map, try to put pages with similar functions in the same navigation map for easy management and maintenance.
  • When using the Safe Args plugin to pass parameters, you should try to use safe types to avoid type conversion errors.

The above is the process of exploring and practicing Android Navigation. For the above example + extension (combined with BottomNavigationView and DrawerLayout), please refer to https://github.com/smallmarker/JetPackPractice

Guess you like

Origin blog.csdn.net/zl_china/article/details/130312001