Android Native - How to use Navigation Component

dimitrilc 3 Tallied Votes 197 Views Share

Introduction

Navigation component is an abstraction on top of FragmentManager, which simplifies navigation between fragments. In this tutorial, we will learn how to use the Navigation component in our App.

Goal

At the end of the tutorial, you would have learned:

  1. How to use the Navigation Component.

Tools Required

  1. Android Studio. The version used in this tutorial is Arctic Fox 2020.3.1 Patch 4.

Prerequisite Knowledge

  1. Basic Android.

Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Remove the default Hello World! TextView.

  3. Add the two string resources below into strings.xml.

     <string name="hello_blank_fragment">Hello blank fragment</string>
     <string name="second_screen">2nd Screen</string>
  4. Add dependencies below to your Module gradle file.

     def nav_version = "2.3.5"
    
     // Kotlin
     implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
     implementation("androidx.navigation:navigation-ui-ktx:$nav_version")

Navigation Component Concept Overview

Before we can start using the Navigation component, we will need to understand the basic concepts of this library.

  1. Navigation Graph: this is an XML file that contains the navigation logics for your Destinations. Android Studio includes a powerful GUI editor to make it easy to visualize your App’s navigation flow.
    10000201000006280000029B667BE17906B2A331.png
  2. Navigation Host: this is an empty activity that houses a NavHostFragment.
  3. NavHostFragment: an object whose primary job is swapping out destinations using its NavController.
  4. NavController: an object with navigate() functions that you can call to direct user navigation.
  5. Destination: where the user navigates to.
  6. Home Destination: the first screen that the user sees.

Create the Nav graph

Now that we are somewhat familiar with the basic concepts, the first thing that we will need to do is to create the Navigation Graph XML file by following the steps below.

  1. Right-click on res > New > Android Resource File.
  2. Generate the nav_graph.xml file based on the settings in the below screenshot. This will place the file under the navigation directory as well as letting Android Studio know that this file can be opened with the Navigation Editor.
    10000201000003FC00000220958C87BFA99664B9.png
  3. Open nav_graph.xml in the Design surface. Notice that there is currently zero Host under the Hosts panel.

100002010000011C000001CF5BAC800E9F0FCB11.png

Designate an Activity as the Navigation Host

We need a Navigation Host to house the NavHostFragment. Follow the steps below to add a Navigation Host:

  1. Open activity_main.xml.

  2. Palette > Container > (select) NavHostFragment.
    100002010000017D000000A5EE21CAEF5E0E7ED1.png

  3. Drag NavHostFragment into the Component Tree.
    100002010000021200000074DFA962A23D54C486.png

  4. Go back to nav_graph.xml in the Design surface, we will now see that acitivty_main now shows up as a Host. This means that the activity_main Navigation Host is associated with this navigation graph (nav_graph.xml).
    10000201000001410000008DB3BDBE45FF5CDA01.png

  5. Now open activity_main.xml in Code view. The app:navGraph attribute that you see here determines that association.

     app:navGraph="@navigation/nav_graph"

Home Destination

Next, we need to create a new destination(Fragment) and designate it as the Home Destination.

  1. Open nav_graph.xml in the Surface view.

  2. Select the New Destination icon > Create new destination.
    10000201000001F8000000FACB9D96E1C75D4CD3.png

  3. We only need a very simple Fragment, so select Fragment (Blank) > Next.
    10000201000001140000011F19E301EDB89A63AB.png

  4. Use BlankFragment as the Fragment Name.

  5. Use fragment_blank as the Fragment Layout Name > Finish.
    10000201000000AB0000009E5DEFB4BC2DC36B0F.png

  6. Designate fragment_blank as the Home Destination by selecting it and then click on the house icon.
    10000201000000F10000006678271276887EAEA0.png

  7. While we are at it, let us modify fragment_blank.xml a little bit more for this tutorial. Remove the default TextView from this fragment.

  8. Convert the Fragment FrameLayout to ConstraintLayout**.
    10000201000001D00000004D2485626060142E12.png

  9. Add a new Button inside ConstraintView using the code below. We will use this Button to navigate to another screen later.

     <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/second_screen"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

Add another Destination

Next, we will need to add another destination(Fragment) to navigate to. Repeat the steps 1-5 in the previous section, but suffix this fragment’s Fragment Name and Fragment Layout Name with a number 2.

Connect the Destinations

Now that we have both destinations, we can start connecting them with actions.

  1. Connect the two fragments by dragging the circle on the right side of blankFragment to blankFragment2.
    10000201000003080000024013900087C3F5632A.png
  2. Alternatively, you can also use the Add Action button to link the two destinations.
    100002010000026B0000030F97CDD7A732D58D8E.png
  3. After connecting the two destinations, you should see something similar to the screenshot below. The Navigation Editor becomes a powerful tool to visualize your Application flow especially when you have a lot of destinations.
    10000201000003F2000002D490D9ED886E8FB6A3.png

Navigate to Destinations

To navigate to another destination, we will need to obtain the reference of the NavController object.

  1. Inside BlankFragment.kt, override the onViewCreated() callback.

     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    
     }
  2. Inside onViewCreated(), get a reference to the NavController object with findNavController(). This function comes from the navigation-fragment-ktx dependency that we added in the beginning.

     val navController = findNavController() //gets the navController
  3. Get a reference to the Button.

     val button = view.findViewById<Button>(R.id.button) //finds the Button
  4. Bind the Button onClickListener to a Navigation action with the code below. Note that we are using the android:id of the <action> element located inside nav_graph.xml here.

     button.setOnClickListener {
        //navigate using the Action ID, not fragment ID
        navController.navigate(R.id.action_blankFragment_to_blankFragment2)
     }

Run the app

We are now ready to run the App. Try clicking on the Button to navigate to the next destination in the flow and then back.

10000022000001D7000003785FCA2C5F17C99608.gif

Solution Code

build.gradle

plugins {
   id 'com.android.application'
   id 'kotlin-android'
}

android {
   compileSdk 31

   defaultConfig {
       applicationId "com.example.daniwebnavigationcomponentsbasics"
       minSdk 21
       targetSdk 31
       versionCode 1
       versionName "1.0"

       testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }

   buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
   }
   compileOptions {
       sourceCompatibility JavaVersion.VERSION_1_8
       targetCompatibility JavaVersion.VERSION_1_8
   }
   kotlinOptions {
       jvmTarget = '1.8'
   }
}

dependencies {
   implementation 'androidx.legacy:legacy-support-v4:1.0.0'
   def nav_version = "2.3.5"

   // Kotlin
   implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
   implementation("androidx.navigation:navigation-ui-ktx:$nav_version")

   implementation 'androidx.core:core-ktx:1.7.0'
   implementation 'androidx.appcompat:appcompat:1.4.0'
   implementation 'com.google.android.material:material:1.4.0'
   implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
   testImplementation 'junit:junit:4.+'
   androidTestImplementation 'androidx.test.ext:junit:1.1.3'
   androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

strings.xml

<resources>
   <string name="app_name">Daniweb Navigation Components Basics</string>
   <string name="hello_blank_fragment">Hello blank fragment</string>
   <string name="second_screen">2nd Screen</string>
</resources>

nav_graph.xml

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

   <fragment
       android:id="@+id/blankFragment"
       android:name="com.example.daniwebnavigationcomponentsbasics.BlankFragment"
       android:label="fragment_blank"
       tools:layout="@layout/fragment_blank" >
       <action
           android:id="@+id/action_blankFragment_to_blankFragment2"
           app:destination="@id/blankFragment2" />
   </fragment>
   <fragment
       android:id="@+id/blankFragment2"
       android:name="com.example.daniwebnavigationcomponentsbasics.BlankFragment2"
       android:label="fragment_blank2"
       tools:layout="@layout/fragment_blank2" />
</navigation>

fragment_blank.xml

<?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:id="@+id/frameLayout"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".BlankFragment" >

   <Button
       android:id="@+id/button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/second_screen"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

fragment_blank2.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".BlankFragment2">

   <!-- TODO: Update blank fragment layout -->
   <TextView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:text="@string/hello_blank_fragment" />

</FrameLayout>

activity_main.xml

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

   <androidx.fragment.app.FragmentContainerView
       android:id="@+id/fragmentContainerView"
       android:name="androidx.navigation.fragment.NavHostFragment"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       app:defaultNavHost="true"
       app:navGraph="@navigation/nav_graph"
       />

</androidx.constraintlayout.widget.ConstraintLayout>

BlankFragment.kt

package com.example.daniwebnavigationcomponentsbasics

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.navigation.fragment.findNavController

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
* A simple [Fragment] subclass.
* Use the [BlankFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class BlankFragment : Fragment() {
   // TODO: Rename and change types of parameters
   private var param1: String? = null
   private var param2: String? = null

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       arguments?.let {
           param1 = it.getString(ARG_PARAM1)
           param2 = it.getString(ARG_PARAM2)
       }
   }

   override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?,
       savedInstanceState: Bundle?
   ): View? {
       // Inflate the layout for this fragment
       return inflater.inflate(R.layout.fragment_blank, container, false)
   }

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
       super.onViewCreated(view, savedInstanceState)

       val navController = findNavController() //gets the navController

       val button = view.findViewById<Button>(R.id.button) //finds the Button

       button.setOnClickListener {
           //navigate using the Action ID, not fragment ID
           navController.navigate(R.id.action_blankFragment_to_blankFragment2)
       }
   }

   companion object {
       /**
        * Use this factory method to create a new instance of
        * this fragment using the provided parameters.
        *
        * @param param1 Parameter 1.
        * @param param2 Parameter 2.
        * @return A new instance of fragment BlankFragment.
        */
       // TODO: Rename and change types and number of parameters
       @JvmStatic
       fun newInstance(param1: String, param2: String) =
           BlankFragment().apply {
               arguments = Bundle().apply {
                   putString(ARG_PARAM1, param1)
                   putString(ARG_PARAM2, param2)
               }
           }
   }
}

Summary

We have learned how to use the Navigation Component.

The full project code can be found here https://github.com/dmitrilc/DaniwebNavigationComponentsBasics

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.