Android Jetpack Navigation, BottomNavigationView with Youtube or Instagram like proper back navigation (fragment back stack)?

Bincy Baby picture Bincy Baby · May 29, 2018 · Viewed 44.4k times · Source

Android Jetpack Navigation, BottomNavigationView with auto fragment back stack on back button click?

What I wanted, after choosing multiple tabs one after another by user and user click on back button app must redirect to the last page he/she opened.

I achieved the same using Android ViewPager, by saving the currently selected item in an ArrayList. Is there any auto back stack after Android Jetpack Navigation Release? I want to achieve it using navigation graph

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".main.MainActivity">

    <fragment
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@+id/navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>

navigation.xml

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

    <item
        android:id="@+id/navigation_home"
        android:icon="@drawable/ic_home"
        android:title="@string/title_home" />

    <item
        android:id="@+id/navigation_people"
        android:icon="@drawable/ic_group"
        android:title="@string/title_people" />

    <item
        android:id="@+id/navigation_organization"
        android:icon="@drawable/ic_organization"
        android:title="@string/title_organization" />

    <item
        android:id="@+id/navigation_business"
        android:icon="@drawable/ic_business"
        android:title="@string/title_business" />

    <item
        android:id="@+id/navigation_tasks"
        android:icon="@drawable/ic_dashboard"
        android:title="@string/title_tasks" />

</menu>

also added

bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))

I got one answer from Levi Moreira, as follows

navigation.setOnNavigationItemSelectedListener {item ->

            onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

        }

But by doing this only happening is that last opened fragment's instance creating again.

Providing proper Back Navigation for BottomNavigationView

Answer

Levi Moreira picture Levi Moreira · May 31, 2018

You don't really need a ViewPager to work with BottomNavigation and the new Navigation architecture component. I have been working in a sample app that uses exactly the two, see here.

The basic concept is this, you have the main activity that will host the BottomNavigationView and that is the Navigation host for your navigation graph, this is how the xml for it look like:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".main.MainActivity">

    <fragment
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@+id/navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>

The navigation Menu (tabs menu) for the BottomNavigationView looks like this:

navigation.xml

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

    <item
        android:id="@+id/navigation_home"
        android:icon="@drawable/ic_home"
        android:title="@string/title_home" />

    <item
        android:id="@+id/navigation_people"
        android:icon="@drawable/ic_group"
        android:title="@string/title_people" />

    <item
        android:id="@+id/navigation_organization"
        android:icon="@drawable/ic_organization"
        android:title="@string/title_organization" />

    <item
        android:id="@+id/navigation_business"
        android:icon="@drawable/ic_business"
        android:title="@string/title_business" />

    <item
        android:id="@+id/navigation_tasks"
        android:icon="@drawable/ic_dashboard"
        android:title="@string/title_tasks" />

</menu>

All of this is just the BottomNavigationView setup. Now to make it work with the Navigation Arch Component you need to go into the navigation graph editor, add all your fragment destinations (in my case I have 5 of them, one for each tab) and set the id of the destination with the same name as the one in the navigation.xml file:

enter image description here

This will tell android to make a link between the tab and the fragment, now every time the user clicks the "Home" tab android will take care of loading up the correct fragment. There is also one piece of kotlin code that needs to be added to your NavHost (the main activity) to wire things up with the BottomNavigationView:

You need to add in your onCreate:

bottomNavigation.setupWithNavController(Navigation.findNavController(this, R.id.my_nav_host_fragment))

This tells android to do the wiring between the Navigation architecture component and the BottomNavigationView. See more in the docs.

To get the same beahvior you have when you use youtube, just add this:

navigation.setOnNavigationItemSelectedListener {item ->

            onNavDestinationSelected(item, Navigation.findNavController(this, R.id.my_nav_host_fragment))

        }

This will make destinations go into the backstack so when you hit the back button, the last visited destination will be popped up.