Navigation Components

The Navigation Architecture component is a part of the new AndroidX package that's introduced since Android SDK 28. This component consists of new guidelines to structure your application, especially navigation between Fragments. Navigation Architecture gets ride of the complex FragmentTranscation by providing its own set of classes to navigate between fragments. Let's first look into the guidelines for Navigation Architecture.


Principles of Navigation

  • The app should have a fixed starting destination.
  • A stack is used to represent the navigation state of an app.
  • The Navigation Up function never exits your app.
  • Up and Back should function identical in all the other cases.
  • Deep linking to a destination or navigating to the same destination should yield the same stack.

The Navigation component consists of three key parts that are described below:

Key Description
Navigation graph An XML resource that contains all navigation-related information in one centralized location. This includes all of the individual content areas within your app, called destinations, as well as the possible paths that a user can take through your app.
NavHost An empty container that displays destinations from your navigation graph. The Navigation component contains a default NavHost implementation, NavHostFragment, that displays fragment destinations.
NavController An object that manages app navigation within a NavHost. The NavController orchestrates the swapping of destination content in the NavHost as users move throughout your app.

Getting Started

Create a new Android Studio Project. Add the following classpath inside the app level build.gradle file:

buildscript {
    
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        ...
        classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-alpha04'
    }
}


and Inside app build.gradle

dependencies {
    ...
    implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha04'
    implementation 'android.arch.navigation:navigation-ui:1.0.0-alpha04'
}


Add plugin into app build.gradle

apply plugin: 'androidx.navigation.safeargs'

Project Structure: In this project consists of a Only on Activity and four fragments. Let's create all xml and fragments one by and try to understand


Creates a navigation folder inside the res with navigation/mobile_navigation.xml, you can decide your activity start with which fragment using app:startDestination

<?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/mobile_navigation"
    app:startDestination="@+id/nav_home">

    <fragment
        android:id="@+id/nav_home"
        android:name="com.theitbulls.navigationex.ui.home.HomeFragment"
        android:label="@string/menu_home"
        tools:layout="@layout/fragment_home">

        <action
            android:id="@+id/action_home_second_fragment"
            app:destination="@id/nav_home_second" />
    </fragment>
    <fragment
        android:id="@+id/nav_home_second"
        android:name="com.theitbulls.navigationex.ui.home.HomeSecondFragment"
        android:label="@string/home_second"
        tools:layout="@layout/fragment_home_second">
        <action
            android:id="@+id/action_home_second_fragment"
            app:destination="@id/nav_home" />

        <argument
            android:name="myArg"
            app:argType="string" />
    </fragment>

    <fragment
        android:id="@+id/nav_gallery"
        android:name="com.theitbulls.navigationex.ui.gallery.GalleryFragment"
        android:label="@string/menu_gallery"
        tools:layout="@layout/fragment_gallery" />

    <fragment
        android:id="@+id/nav_slideshow"
        android:name="com.theitbulls.navigationex.ui.slideshow.SlideshowFragment"
        android:label="@string/menu_slideshow"
        tools:layout="@layout/fragment_slideshow" />
</navigation>

mobile_nagivation.xml flow can be understand by following image, where we can see navigation from nav_home to nav_home_second fragment and again back to nav_home using action as define in mobile_nagivation.xml


Navigation Host Fragment: Inside the Activity layout, we define a Navigation Host Fragment. In our project, we've set the navigation host fragment inside the activity_main.xml layout as shown below:

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <!--Note: it's very important below include code must be above than NavigationView, otherwise navigation won't work-->
    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>


app_bar_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/content_main" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

content_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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/app_bar_main">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>


Let's create fragment_home.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".fragments.HomeFragment">

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btnSecondFrag"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/text_home"
        android:layout_centerHorizontal="true"
        android:text="Second Fragment" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/text_home"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Home Fragment"
        android:textSize="20sp" />
</RelativeLayout>

Now most important create HomeFragment.java where we will handle action define in mobile_navigation.xml for home_fragment:

HomeFragment.java
package com.theitbulls.navigationex.fragments;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatButton;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.navigation.Navigation;

import com.theitbulls.navigationex.R;

public class HomeFragment extends Fragment {
    private Context mContext;
    private FragmentActivity activity;

    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_home, container, false);

        View.OnClickListener homeSecondView = Navigation.createNavigateOnClickListener(R.id.action_home_second_fragment);
        AppCompatButton btnSecondFrag = root.findViewById(R.id.btnSecondFrag);
        btnSecondFrag.setOnClickListener(homeSecondView);
        return root;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // save this context for future use, because some time in fragment getContext() return null
        this.activity = getActivity();
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        // save this context for future use, because some time in fragment getContext() return null
        this.mContext = context;
    }
}

we can handle action like below code too:

final NavController navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
AppCompatButton btnSecondFrag = root.findViewById(R.id.btnSecondFrag);

btnSecondFrag.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
                navController.navigate(R.id.action_home_second_fragment);
        }
});


How to pass arguments to fragment in navigation. Note: arguments will be same defined in mobile_navigation.xml

final NavController navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
AppCompatButton btnSecondFrag = root.findViewById(R.id.btnSecondFrag);

final Bundle bundle = new Bundle();
bundle.putString("myArg", "The It Bulls");
btnSecondFrag.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
                navController.navigate(R.id.action_home_second_fragment, bundle);
        }
});

Download: Navigation Complete Example