Navigation is architecture component part of Android Jetpack which handles everything needed for an app navigation.
Easy to implement and whole app navigation at one place, with visual code editor which connects different destinations using actions.

Navigation library comes with these features:

  1. SafeArgs – Using gradle plugin we can pass data between destinations(fragments) which is type safe at time of navigation.
  2. Add animations and transition with ease while navigating with actions.
  3. Handles fragment transaction.
  4. Handles up and back button navigation correctly
  5. Supports other patterns like navigation drawer, action bar, menu’s and simple button clicks that too with less code, etc.

With image below and explanation let’s see how navigation works in app.

Navigation component in our app needs three parts which should work together.
1. Navigation graph, a visual form of showing all destinations and how they are connected by actions in our app of xml resource type. Image above itself is a Navigation graph which we will build.
2. NavHost, this contains a default NavHostFragment which we will hookup with our activity to display fragments properly.
3. NavController, a controller which is set in NavHost itself to handle all fragment transactions when we navigate in app.


Watch this video to see how our app looks like once we complete.

We are going to build an app which shows a fragment with list at first launch and opens into another fragment to shows details of item when it’s clicked, along with that a menu item with ActionBar which opens our settings fragment. And we pass data between these destination(fragments) using SafeArgs. User clicks on item in list, menu and up button navigation is implemented using navigation components only.

1. Add dependencies to project

We need to add dependencies for navigation in our project

implementation "androidx.navigation:navigation-fragment:2.1.0" 
implementation "androidx.navigation:navigation-ui:2.1.0"
apply plugin: "androidx.navigation.safeargs"

Open build.gradle ( Project ) directory and this plugin under existing dependency.

classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0"

Now sync your project to import all required classes.

2. Create navigation graph

Let’s create a navigation graph for our app which visualize all destinations at one place with connections between them.

In your project under res folder right click and select New Android Resource File. On dialog box enter
file name : navigation
Resource type: Navigation and click ok.

Right now our navigation file has no available destinations and connections so we have to create some fragments and layouts. We will do this below, for now leave this empty.

3. Add NavHostFragment to activity

NavHost has default fragment which is NavHostFragment. We hook this into activity a place where it acts as a container for all fragments to show. NavHost makes use of NavController object to add or remove fragments when we navigate in app.
So let’s add a NavHost for our app in activity_main.xml res file which is NavHostFragment.

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

    <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:navGraph="@navigation/navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

We will see what’s happening here.

  • android:id=”@+id/nav_host_fragment”
    We declare attribute id to hook NavigationController with MainActivity.java class
  • android:name=”androidx.navigation.fragment.NavHostFragment”
    Attribute name will change behavior of simple fragment to NavHostFragment
  • app:defaultNavHost=”true”
    Setting value true to this attribute will make this fragment to behave default NavHostFragment.
  • app:navGraph=”@navigation/navigation”
    Attach our app navigation graph to this NavHostFragment

Okay this is our final layout after changes.

4. Add resources into project

Change or add these xml resources from below to your project

colors.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#ff5722</color>
    <color name="colorPrimaryDark">#e64a19</color>
    <color name="colorCardBackground">#ffccbc</color>
    <color name="colorIconText">#ff6e40</color>
    <color name="colorLightBackground">#fbe9e7</color>
</resources>

string.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Navigation Component</string>
    <string name="settings">Settings</string>
    <string name="direction_arrow_back">Arrow back</string>
</resources>

styles.xml:

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    </style>
</resources>

We need sample images to show in our list, I got some for you. Download those images from this link and add them to your drawable folder.

5. Create model and adapter class

Since we are showing a list of items we need a java model class, a adapter java class for that we will go with RecyclerView with grid layout for that we need a xml layout for each item in grid item, we are gonna speak less about these because our priority is learning navigation component.

5A. Navigator.java , item_navigator.xml

Create a java model class with name Navigator and add this below code.
Observe that we make this class Parcelable cause we are passing data between destinations using Safe Args.
Since we are showing a image and text in our item we are using class with two constructors, integer for icon and string variable for text.

Navigator.java:

import android.os.Parcel;
import android.os.Parcelable;

public class Navigator implements Parcelable {
    
    private int mNavigatorIcon;
    private String mNavigatorName;

    public Navigator(int navigatorIcon, String navigatorName) {
        this.mNavigatorIcon = navigatorIcon;
        this.mNavigatorName = navigatorName;
    }

    public int getNavigatorIcon() {
        return mNavigatorIcon;
    }

    public String getNavigatorName() {
        return mNavigatorName;
    }

    private Navigator(Parcel in) {
        mNavigatorIcon = in.readInt();
        mNavigatorName = in.readString();
    }

    public static final Creator<Navigator> CREATOR = new Creator<Navigator>() {
        @Override
        public Navigator createFromParcel(Parcel in) {
            return new Navigator(in);
        }

        @Override
        public Navigator[] newArray(int size) {
            return new Navigator[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeInt(mNavigatorIcon);
        parcel.writeString(mNavigatorName);
    }
}

Create a layout under res –> layout folder and name it item_navigator

item_navigator.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 
    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="wrap_content"
    app:cardBackgroundColor="@color/colorCardBackground"
    app:cardCornerRadius="4dp"
    app:cardElevation="4dp"
    app:cardUseCompatPadding="true">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp">

        <ImageView
            android:id="@+id/direction_icon_image_item_view"
            android:layout_width="120dp"
            android:layout_height="120dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@drawable/ic_arrow_back" />

        <TextView
            android:id="@+id/direction_name_text_item_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:textColor="@color/colorIconText"
            android:textSize="20sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="@id/direction_icon_image_item_view"
            app:layout_constraintStart_toStartOf="@id/direction_icon_image_item_view"
            app:layout_constraintTop_toBottomOf="@id/direction_icon_image_item_view"
            tools:text="@string/direction_arrow_back" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

6B. NavigatorAdapter.java

Create class NavigtorAdapter and extend it to use RecyclerView, add this below code to the class.

NavigatorAdapter.java:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class NavigatorAdapter extends RecyclerView.Adapter<NavigatorAdapter.NavigatorViewHolder> {

    private Context mContext;
    private List<Navigator> mNavigatorList;
    private NavigatorAdapterListener mListener;

    NavigatorAdapter(Context context, List<Navigator> navigatorList, NavigatorAdapterListener listener) {
        this.mContext = context;
        this.mNavigatorList = navigatorList;
        this.mListener = listener;
    }

    public interface NavigatorAdapterListener {
        void onNavigatorSelected(Navigator navigator, View view);
    }

    class NavigatorViewHolder extends RecyclerView.ViewHolder {

        private ImageView mNavigatorIconImageView;
        private TextView mNavigatorNameTextView;

        NavigatorViewHolder(@NonNull final View itemView) {
            super(itemView);
            mNavigatorIconImageView = itemView.findViewById(R.id.direction_icon_image_item_view);
            mNavigatorNameTextView = itemView.findViewById(R.id.direction_name_text_item_view);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mListener.onNavigatorSelected(mNavigatorList.get(getAdapterPosition()), itemView);
                }
            });
        }
    }

    @NonNull
    @Override
    public NavigatorViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_navigator, parent, false);
        return new NavigatorViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull NavigatorViewHolder holder, int position) {
        Navigator navigator = mNavigatorList.get(position);
        holder.mNavigatorIconImageView.setImageResource(navigator.getNavigatorIcon());
        holder.mNavigatorNameTextView.setText(navigator.getNavigatorName());
    }
   
    @Override
    public int getItemCount() {
        return mNavigatorList.size();
    }
}

6. Create and add menu item for settings

We show a menu icon setting on clicking it, will opens settings fragment which we will next step.
Under res right click and add New Android Resource File.
File name: menu_settings
Resource type: Menu and click ok
Add this code below to your xml file you created

menu_settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/menu_item_settings"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings"
        app:showAsAction="always" />
</menu>

7. Create fragments and layouts

We have 3 destinations in our app, so we need 3 fragments.
ListFragment shows list of grid items when user opens app for first time and this will be our first destination
DetailFragment shows details of each item when user clicks and this will be our second destination
SettingsFragment shows settings screen for our app and this is our third destination and we navigate here by clicking menu item from first destination which is ListFragment
Okay let’s create three fragments with layouts and we connect those soon.

Create fragment with name ListFragment and it’s layout

ListFragment.java:

import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;


/**
 * A simple {@link Fragment} subclass.
 */
public class ListFragment extends Fragment {

    private RecyclerView mRecyclerView;
    private List<Navigator> mNavigatorList;

    public ListFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_list, container, false);
        mRecyclerView = view.findViewById(R.id.navigator_recycler_view);

        setHasOptionsMenu(true);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        loadData();
        NavigatorAdapter navigatorAdapter = new NavigatorAdapter(getContext(), mNavigatorList, new NavigatorListener());
        mRecyclerView.setAdapter(navigatorAdapter);
    }

    private void loadData() {
        mNavigatorList = new ArrayList<>();
        mNavigatorList.add(new Navigator(R.drawable.ic_arrow_back, "Back"));
        mNavigatorList.add(new Navigator(R.drawable.ic_arrow_downward, "Downward"));
        mNavigatorList.add(new Navigator(R.drawable.ic_navigation, "Navigation"));
        mNavigatorList.add(new Navigator(R.drawable.ic_near_me, "Near me"));
        mNavigatorList.add(new Navigator(R.drawable.ic_arrow_forward, "Forward"));
        mNavigatorList.add(new Navigator(R.drawable.ic_arrow_upward, "Upward"));
        mNavigatorList.add(new Navigator(R.drawable.ic_chevron_left, "Chevron left"));
        mNavigatorList.add(new Navigator(R.drawable.ic_chevron_right, "Chevron right"));
        mNavigatorList.add(new Navigator(R.drawable.ic_expand_less, "Expand less"));
        mNavigatorList.add(new Navigator(R.drawable.ic_expand_more, "Expand more"));
        mNavigatorList.add(new Navigator(R.drawable.ic_first_page, "First page"));
        mNavigatorList.add(new Navigator(R.drawable.ic_last_page, "Last page"));
        mNavigatorList.add(new Navigator(R.drawable.ic_subdirectory_arrow_left, "Subdirectory left"));
        mNavigatorList.add(new Navigator(R.drawable.ic_subdirectory_arrow_right, "Subdirectory right"));
    }

    private class NavigatorListener implements NavigatorAdapter.NavigatorAdapterListener {

        @Override
        public void onNavigatorSelected(Navigator navigator, View view) {
            // To be implemented
        }
    }

    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.menu_settings, menu);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {

        int id = item.getItemId();
        if (id == R.id.menu_item_settings && getView() != null) {
            // To be implemented
        }
        return super.onOptionsItemSelected(item);
    }
}

fragment_list.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    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"
    android:background="@color/colorLightBackground"
    tools:context=".ListFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/navigator_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
        app:spanCount="2"
        tools:listitem="@layout/item_navigator" />

</FrameLayout>

Create fragment with name DetailFragment and it’s layout

DetailFragment.java:

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;


/**
 * A simple {@link Fragment} subclass.
 */
public class DetailFragment extends Fragment {

    public DetailFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_detail, container, false);

        ImageView directionsImageView = view.findViewById(R.id.detail_directions_icon_image_view);
        TextView directionsTextView = view.findViewById(R.id.detail_directions_name_text_view);

        return view;
    }
}

fragment_detail.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"
    android:background="@color/colorLightBackground"
    tools:context=".DetailFragment">

    <ImageView
        android:id="@+id/detail_directions_icon_image_view"
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:src="@drawable/ic_arrow_back" />

    <TextView
        android:id="@+id/detail_directions_name_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="@string/direction_arrow_back"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4"
        android:textColor="@color/colorIconText"
        app:layout_constraintEnd_toEndOf="@id/detail_directions_icon_image_view"
        app:layout_constraintStart_toStartOf="@id/detail_directions_icon_image_view"
        app:layout_constraintTop_toBottomOf="@id/detail_directions_icon_image_view" />

</androidx.constraintlayout.widget.ConstraintLayout>

Create fragment with name SettingsFragment and it’s layout

SettingsFragment.java:

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * A simple {@link Fragment} subclass.
 */
public class SettingsFragment extends Fragment {
    
    public SettingsFragment() {
        // Required empty public constructor
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_settings, container, false);
    }
}

fragment_settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    android:background="@color/colorLightBackground"
    tools:context=".SettingsFragment">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/ic_settings"
        android:tint="@color/colorIconText" />

</androidx.constraintlayout.widget.ConstraintLayout>

8. Connect destinations with actions

We created two destinations, all we need to do is add them in navigation graph and connect them with actions using visual editor which is simple.
Let’s connect the ListFragment to DetailFragment first and then we pass arguments and we receive them as bundle.

8A. ListFragment to DetailFragment

Open your navigation.xml layout file which we created in step 3.
From the bottom navigate to design tab.
You will notice we have activity_main to be default host.
Now click on the icon New Destination, from the list select fragment_list and click once more and select fragment_detail.
Now you can drag and place destinations properly or just click Auto Arrange icon to adjust automatically.

Now we will pass arguments to DetailFragment from ListFragment.
Select detailFragment destination in navigation graph and to the right side attribute window.
Click + icon in Arguments to add one.
Name: detailFragmentArguments
Type: Custom Parcelable then choose Navigator class which was implemented Parcelable.
Click Add finally.

You can check xml code which is auto-generated whenever you make changes in visual editor and vice-versa. This is how your navigation xml looks right now.

navigation.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/navigation"
    app:startDestination="@id/listFragment">

    <fragment
        android:id="@+id/listFragment"
        android:name="com.developersbreach.navigator.ListFragment"
        android:label="Navigator"
        tools:layout="@layout/fragment_list">
        <action
            android:id="@+id/action_listFragment_to_detailFragment"
            app:destination="@id/detailFragment" />
    </fragment>

    <fragment
        android:id="@+id/detailFragment"
        android:name="com.developersbreach.navigator.DetailFragment"
        android:label="Detail"
        tools:layout="@layout/fragment_detail">
        <argument
            android:name="detailFragmentArguments"
            app:argType="com.developersbreach.navigator.Navigator" />
    </fragment>

</navigation>

Rebuild your project at this stage. Because android studio will auto generate required methods to work SafeArgs as expected.

Run your project and you can see a list of grid items and menu showing with ActionBar. But clicking on those items yet does nothing cause we need to do this using NavController which swaps all our fragments. So we make changes in java classes.

In ListFragment class we need at add NavController when user clicks item, we implemented a listener for this using onNavigatorSelected. Add this code to that.

NavDirections action = ListFragmentDirections.actionListFragmentToDetailFragment(navigator);
Navigation.findNavController(view).navigate(action);

Using Navigation object we find navigation controller with view then we will call navigate with it’s action name and pass argument to open correct item.
You can change this action name “actionListFragmentToDetailFragment” of your choice in navigation.xml file and make both match properly, after making changes.

private class NavigatorListener implements NavigatorAdapter.NavigatorAdapterListener {
    @Override
    public void onNavigatorSelected(Navigator navigator, View view) {
        NavDirections action = ListFragmentDirections.actionListFragmentToDetailFragment(navigator);
        Navigation.findNavController(view).navigate(action);
    }
}

Now listener works as expected and we can navigate to detailFragment but we have to get data as bundle, let’s do that.

DetailFragment.class:

Inside onCreate of fragment class add this code to get data we need from bundle.

// Notice that object DetailFragmentArgs is auto generated by android studio only if proper build happens.
Navigator navigator = DetailFragmentArgs.fromBundle(Objects.requireNonNull(getArguments())).getDetailFragmentArguments();

// Now we will get data from parcelable class Navigator.java and set it to our views
directionsImageView.setImageResource(navigator.getNavigatorIcon());
directionsTextView.setText(navigator.getNavigatorName());

After making those changes to your fragment class this is how it looks.

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.Objects;

/**
 * A simple {@link Fragment} subclass.
 */
public class DetailFragment extends Fragment {

    public DetailFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_detail, container, false);

        ImageView directionsImageView = view.findViewById(R.id.detail_directions_icon_image_view);
        TextView directionsTextView = view.findViewById(R.id.detail_directions_name_text_view);

        Navigator navigator = DetailFragmentArgs.fromBundle(Objects.requireNonNull(getArguments())).getDetailFragmentArguments();

        directionsImageView.setImageResource(navigator.getNavigatorIcon());
        directionsTextView.setText(navigator.getNavigatorName());

        return view;
    }
}

Now run your app, you must be able to see data in detailFragment destination. Now let’s work on to menu item to open settings fragment.

8B. ListFragment to SettingsFragment

Let’s connect listFragment with settingsFragment, whenever we click on menu item this navigation should happen.
In the existing graph add a new destination by clicking the icon and select fragment_settings. Connect listFragment to settingsFragment by dragging towards layout just.

This is after making changes navigation graph.

navigation.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/navigation"
    app:startDestination="@id/listFragment">

    <fragment
        android:id="@+id/listFragment"
        android:name="com.developersbreach.navigator.ListFragment"
        android:label="Navigator"
        tools:layout="@layout/fragment_list">
        <action
            android:id="@+id/action_listFragment_to_detailFragment"
            app:destination="@id/detailFragment" />
        <action
            android:id="@+id/action_listFragment_to_settingsFragment"
            app:destination="@id/settingsFragment" />
    </fragment>

    <fragment
        android:id="@+id/detailFragment"
        android:name="com.developersbreach.navigator.DetailFragment"
        android:label="Detail"
        tools:layout="@layout/fragment_detail">
        <argument
            android:name="detailFragmentArguments"
            app:argType="com.developersbreach.navigator.Navigator" />
    </fragment>

    <fragment
        android:id="@+id/settingsFragment"
        android:name="com.developersbreach.navigator.SettingsFragment"
        android:label="Settings"
        tools:layout="@layout/fragment_settings" />

</navigation>

Now Rebuild your project to import Direction objects and actions.

Now let’s hook with fragment for click to work using NavController like we did before for navigating to detailFragment.

ListFragment.java:

NavDirections action = ListFragmentDirections.actionListFragmentToSettingsFragment();
Navigation.findNavController(getView()).navigate(action);

That’s it. now click should work on settings menu item as expected and this your final fragment class after all the changes.

import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavDirections;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;

/**
 * A simple {@link Fragment} subclass.
 */
public class ListFragment extends Fragment {

    private RecyclerView mRecyclerView;
    private List<Navigator> mNavigatorList;

    public ListFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_list, container, false);
        mRecyclerView = view.findViewById(R.id.navigator_recycler_view);

        setHasOptionsMenu(true);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        loadData();
        NavigatorAdapter navigatorAdapter = new NavigatorAdapter(getContext(), mNavigatorList, new NavigatorListener());
        mRecyclerView.setAdapter(navigatorAdapter);
    }

    private class NavigatorListener implements NavigatorAdapter.NavigatorAdapterListener {

        @Override
        public void onNavigatorSelected(Navigator navigator, View view) {
            NavDirections action = ListFragmentDirections.actionListFragmentToDetailFragment(navigator);
            Navigation.findNavController(view).navigate(action);
        }
    }

    private void loadData() {
        mNavigatorList = new ArrayList<>();
        mNavigatorList.add(new Navigator(R.drawable.ic_arrow_back, "Back"));
        mNavigatorList.add(new Navigator(R.drawable.ic_arrow_downward, "Downward"));
        mNavigatorList.add(new Navigator(R.drawable.ic_navigation, "Navigation"));
        mNavigatorList.add(new Navigator(R.drawable.ic_near_me, "Near me"));
        mNavigatorList.add(new Navigator(R.drawable.ic_arrow_forward, "Forward"));
        mNavigatorList.add(new Navigator(R.drawable.ic_arrow_upward, "Upward"));
        mNavigatorList.add(new Navigator(R.drawable.ic_chevron_left, "Chevron left"));
        mNavigatorList.add(new Navigator(R.drawable.ic_chevron_right, "Chevron right"));
        mNavigatorList.add(new Navigator(R.drawable.ic_expand_less, "Expand less"));
        mNavigatorList.add(new Navigator(R.drawable.ic_expand_more, "Expand more"));
        mNavigatorList.add(new Navigator(R.drawable.ic_first_page, "First page"));
        mNavigatorList.add(new Navigator(R.drawable.ic_last_page, "Last page"));
        mNavigatorList.add(new Navigator(R.drawable.ic_subdirectory_arrow_left, "Subdirectory left"));
        mNavigatorList.add(new Navigator(R.drawable.ic_subdirectory_arrow_right, "Subdirectory right"));
    }

    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.menu_settings, menu);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {

        int id = item.getItemId();
        if (id == R.id.menu_item_settings && getView() != null) {
            NavDirections action = ListFragmentDirections.actionListFragmentToSettingsFragment();
            Navigation.findNavController(getView()).navigate(action);
        }
        return super.onOptionsItemSelected(item);
    }
}

9. Add up button to navigate back

Notice our app is missing up button, so far we navigate back using back button. So let’s add Up button, because it will never navigate away user from whole app.

MainActivity.java:

First we will find the NavController with id that we declared in activity_main.xml file which is nav_host_fragment.

NavController navigationController  = Navigation.findNavController(this, R.id.nav_host_fragment);

This line of code is self descriptive. We just set app ActionBar with NavController to NavigationUI and pass this as our activity.

NavigationUI.setupActionBarWithNavController(this, navigationController);

Well this ain’t enough. so far with this two lines of code we are able to show the Up button but it doesn’t work once clicked. For that we just need to override with onSupportNavigateUp() to our activity navigationController, so this is how we do that.

@Override
public boolean onSupportNavigateUp() {
    NavController navigationController = Navigation.findNavController(this, R.id.nav_host_fragment);
    return navigationController.navigateUp();
}

Run your app, now it will work as expected. This is our final MainActivity class.

MainActivity.java

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;

public class MainActivity extends AppCompatActivity {

    private NavController mNavigationController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mNavigationController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, mNavigationController);
    }

    @Override
    public boolean onSupportNavigateUp() {
        mNavigationController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return mNavigationController.navigateUp();
    }
}

Ugh, this took so long but we made it finally. If you found any difficulty in following me, below links will be helpful for you with whole project.
Please don’t forget to subscribe to my blog, you will receive notification to you mail whenever I write new post.

Link to Github
Link to download and import project.
Navigation component with kotlin – here
Navigation with Livedata and ViewModel – here.

Get started with the Navigation component
Migrate project to navigation
Design Navigation Graphs
Codelabs – Navigation Component

Rajasekhar K E


Hi ! I’m Rajasekhar a Programmer who does Android Development, Creative & Technical writing, Kotlin enthusiast and Engineering graduate. I learn from Open Source and always happy to assist others with my work. I spend most of time Training, Assisting & Mentoring students who are absolute Beginners in android development. I’m also running my startup named Developers Breach which mostly works on contributing to open source.

Here We Go Again : (

if (article == helpful) {
    println("Like and subscribe to blog newsletter.")
} else {
    println("Let me know what i should blog on.")
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.