14

I have successfully integrated the bottom navigation with the latest android architecture navigation components. The following is my complete code.

  1. Navigation
<?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/navigation_home">

    <fragment
        android:id="@+id/navigation_home"
        android:name="in.zedone.bottomsample.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/navigation_saloons"
        android:name="in.zedone.bottomsample.ui.saloons.SaloonsFragment"
        android:label="@string/title_saloon"
        tools:layout="@layout/fragment_saloons" />

    <fragment
        android:id="@+id/navigation_offers"
        android:name="in.zedone.bottomsample.ui.offers.OffersFragment"
        android:label="@string/title_offer"
        tools:layout="@layout/fragment_offers" />

    <fragment
        android:id="@+id/navigation_account"
        android:name="in.zedone.bottomsample.ui.account.AccountFragment"
        android:label="@string/title_account"
        tools:layout="@layout/fragment_account" />

</navigation>
  1. BottomNavigationView
<com.google.android.material.bottomnavigation.BottomNavigationView
     android:id="@+id/nav_view"
     android:layout_width="0dp"
     android:layout_height="wrap_content"
     android:layout_marginStart="0dp"
     android:layout_marginEnd="0dp"
     android:background="?android:attr/windowBackground"
     app:labelVisibilityMode="labeled"
     app:itemTextAppearanceActive="@style/BottomNavigationView.Active"
     app:itemTextAppearanceInactive="@style/BottomNavigationView"
     app:layout_constraintBottom_toBottomOf="parent"
     app:layout_constraintLeft_toLeftOf="parent"
     app:layout_constraintRight_toRightOf="parent"
     app:menu="@menu/bottom_nav_menu" />
  1. MainActivity
BottomNavigationView navView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                R.id.navigation_home, R.id.navigation_saloons, R.id.navigation_offers,R.id.navigation_account)
                .build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);

Now how can I add transition/animation on select each tab/fragment in bottom navigation?

Habeeb Rahman
  • 405
  • 1
  • 5
  • 15

4 Answers4

17

Instead of using setupWithNavController function, follow this way.

First, create your NavOptions which include animation shown below.

val options = NavOptions.Builder()
            .setLaunchSingleTop(true)
            .setEnterAnim(R.anim.enter_from_bottom)
            .setExitAnim(R.anim.exit_to_top)
            .setPopEnterAnim(R.anim.enter_from_top)
            .setPopExitAnim(R.anim.exit_to_bottom)
            .setPopUpTo(navController.graph.startDestination, false)
            .build()

Then use setOnNavigationItemSelectedListener to navigate with animation like that.

bottomNavigationView.setOnNavigationItemSelectedListener { item ->
        when(item.itemId) {
            R.id.fragmentFirst -> {
                navController.navigate(R.id.fragmentFirst,null,options)
            }
            R.id.fragmentSecond -> {
                navController.navigate(R.id.fragmentSecond,null,options)
            }
            R.id.fragmentThird -> {
                navController.navigate(R.id.fragmentThird,null,options)
            }
        }
         true
    }

Finally, you should prevent same item selection case so you can add below code.

bottomNavigationView.setOnNavigationItemReselectedListener { item ->
        return@setOnNavigationItemReselectedListener

I used bottomNavigation like that in my project to add animation for page transitions. I hope it helped.

thegirlincode
  • 335
  • 4
  • 9
  • 2
    This is the only easy and bugfree solution i could find, works perfectly and should be the accepted answer. Just remember to remove "setupwithnavcontroller". Thanks! – m.i.n.a.r. Mar 02 '21 at 18:15
  • Where do I get the animations from? "R.anim.enter_from" etc are all red" – Andrew Mar 16 '21 at 15:56
  • 1
    Hi @Andrew, you can add anim folder into res directory and add your animations via searching with google then change names with yours. For example: enter_from_bottom.xml should be – thegirlincode Mar 18 '21 at 11:27
  • @thegirlincode with above code, if I select a Fragment now and then after sometime if I select the same Fragment again then a new object Fragment is getting created instead of showing the old object of the Fragment, how can I achieve this?? – Prateek Gupta Mar 04 '23 at 15:16
4

its work with BottomNavigationView's siblings fragment also(JetPack Navigation Components)

// FragmentA.kt
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  exitTransition = MaterialFadeThrough()
}


// FragmentB.kt
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  enterTransition = MaterialFadeThrough()
}
1

You can also do it with keeping setupWithNavController by overriding onCreateAnimation() in each Fragment and checking whether or not you're entering or exiting the Fragment with the enter parameter and then creating the appropriate Animation with AnimationUtils.loadAnimation(context, animationId).

override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
    return if (enter) {
        AnimationUtils.loadAnimation(context, R.anim.fade_in)
    } else {
        AnimationUtils.loadAnimation(context, R.anim.fade_out)
    }
}

EDIT as a response to iloo:

According to Material Design (https://material.io/components/bottom-navigation#behavior) you shouldn't be doing that in the first place, but I guess it's still achievable.

Since when using setupWithNavController all destinations are top level there will ever be only one previousBackStackEntry whose destination points to the home destination, therefore previousBackStackEntry is not much of a help in figuring out which Fragment are you coming from.

Other approach could be to have a public variable in the Activity, where you will store the last destination you were in and set that variable in each Fragment upon resuming. And you can check that variable in onAnimationCreate to know which Fragment you're coming from and apply an appropriate animation.

J.Grbo
  • 435
  • 5
  • 11
  • To me, this approach works well when animation does not depend on the item's position in bottom navigation menu, relatively to other items. Fading in and out works since it doesn't have direction. But if you want the views to slide in from left or right, and slide out to left or right, depending on its "position on the line of views", the incoming parameters are not enough. Can this approach be elaborated on to be used for cases like this? – iloo Apr 29 '21 at 06:44
-2

I'm kinda new to the Android Navigation but this is what I think you can try if you like to change the default animation

put those file in your anim directory

nav_default_enter_anim.xml
nav_default_exit_anim.xml
nav_default_pop_enter_anim.xml
nav_default_pop_exit_anim.xml

and the default animation for the transactions will change to the animation you put in the above files.