33

i am building an application in which i have a BottomNavigationView. Everything works fine until i get to the Activity.

The Navigation is this:

enter image description here

The problem is that it has this default animation so it pushes the active element everytime higher than the rest.

Another Example:

enter image description here

So my question is how to get rid of this default animation and every item is aligned when i switch between them?

My code:

public class MainActivity extends AppCompatActivity {
    private BottomNavigationView bottomNavigationView;
    private Fragment fragment;
    private FragmentManager fragmentManager;
    private FragmentTransaction transaction;

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

    private void setupBottomBar() {
        bottomNavigationView = (BottomNavigationView)findViewById(R.id.bottomBar);
        fragmentManager = getSupportFragmentManager();
        fragment = new CardDeckFragment();
        transaction = fragmentManager.beginTransaction();
        transaction.replace(R.id.activity_main, fragment).commit();
        bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()){
                    case R.id.action_card_deck:{
                        Toast.makeText(MainActivity.this, "Card Deck Selected", Toast.LENGTH_SHORT).show();
                        fragment = new CardDeckFragment();
                        break;
                    }
                    case R.id.action_favorites:{
                        Toast.makeText(MainActivity.this, "Favorites Selected", Toast.LENGTH_SHORT).show();
                        fragment = new FavoritesFragment();
                        break;
                    }
                    case R.id.action_favorites_another:{
                        Toast.makeText(MainActivity.this, "Image Selected", Toast.LENGTH_SHORT).show();
                        fragment = new ImageFragment();
                        break;
                    }
                    case R.id.action_profile:{
                        Toast.makeText(MainActivity.this, "Profile Selected", Toast.LENGTH_SHORT).show();
                        fragment = new ProfileFragment();
                        break;
                    }
                    case R.id.action_menu:{
                        Toast.makeText(MainActivity.this, "Menu Selected", Toast.LENGTH_SHORT).show();
                        fragment = new MenuFragment();
                        break;
                    }
                }

                transaction = fragmentManager.beginTransaction();
                transaction.replace(R.id.activity_main, fragment).commit();
                return true;
            }
        });



    }
}

and my layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.realtimegaming.androidnative.testproject.MainActivity">

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottomBar"
        android:layout_alignParentBottom="true"
        android:background="@color/brown"
        android:layout_gravity="bottom"
        android:gravity="bottom"
        android:layout_marginTop="?attr/actionBarSize"
        app:itemBackground="@color/colorPrimary"
        app:menu="@menu/bottom_navigation_main"
        app:itemIconTint="@color/white"
        android:fitsSystemWindows="true"
        android:animateLayoutChanges="false"
        android:splitMotionEvents="false"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</RelativeLayout>
  • Have you tried removing *animateLayoutChanges* and *splitMotionEvents* properties ? – Jay Rathod Dec 05 '16 at 11:00
  • no...can you give an example? –  Dec 05 '16 at 11:01
  • Hopefully google will soon release [this version](https://github.com/material-components/material-components-android/blob/master/lib/src/android/support/design/widget/BottomNavigationView.java) of the BottomNavigationView which includes a method to setShiftingMode both in xml and java. – MidasLefko Oct 22 '17 at 11:15
  • For those who find this, here is the issue in the Android Support Library issue tracker for adding this as a feature of the BottomNavigationView : https://issuetracker.google.com/issues/37125827 – Brandon Haugen Mar 01 '18 at 15:34
  • https://stackoverflow.com/a/47544809/7783141 – azamatikus Mar 22 '18 at 21:57
  • If you want to **completely** remove animation: https://stackoverflow.com/a/51052247/2352699 – Fred Porciúncula Jun 26 '18 at 22:38
  • In support library 28.0.0 you can use this: `` – extmkv Jul 14 '18 at 10:01

1 Answers1

75

Ok i found a way in case it helps someone else. So by default BottomNavigationView add shiftingmode = true when its more than 3 items.

At this moment you cannot change it through existing API and the only way to disable shift mode is to use reflection.

So we can use this helper to get rid of this:

class BottomNavigationViewHelper {

    static void removeShiftMode(BottomNavigationView view) {
        BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
        try {
            Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
            shiftingMode.setAccessible(true);
            shiftingMode.setBoolean(menuView, false);
            shiftingMode.setAccessible(false);
            for (int i = 0; i < menuView.getChildCount(); i++) {
                BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
                item.setShiftingMode(false);
                // set once again checked value, so view will be updated
                item.setChecked(item.getItemData().isChecked());
            }
        } catch (NoSuchFieldException e) {
            Log.e("ERROR NO SUCH FIELD", "Unable to get shift mode field");
        } catch (IllegalAccessException e) {
            Log.e("ERROR ILLEGAL ALG", "Unable to change value of shift mode");
        }
    }
}

and then use it like this:

BottomNavigationView bottomNavigationView = (BottomNavigationView)findViewById(R.id.bottomBar);
BottomNavigationViewHelper.removeShiftMode(bottomNavigationView);

Hope this helps someone with the same problem with me!!!

Remember, you'll need to execute this method each time you change menu items in your BottomNavigationView.

UPDATE

As per different stackoverflow question, you also need to update proguard configuration file (e.g. proguard-rules.pro), code above uses reflection and won't work if proguard obfuscate the mShiftingMode field.

-keepclassmembers class android.support.design.internal.BottomNavigationMenuView { 
    boolean mShiftingMode; 
}
Community
  • 1
  • 1
  • It helped! And this is also the right answer. Thanks a lot! – Muppet Jan 08 '17 at 23:12
  • this answer helped.. It doesn't take animation out completely.. can still see a small animation but better than the library default behaviour – Srikanth Pai May 02 '17 at 06:09
  • 94
    God, Android development is crazy... – Chris Jeon May 05 '17 at 15:55
  • Yeah, reflection worked for me. Tried with support library v25.3.1. – Prashant Jul 04 '17 at 11:33
  • 2
    this doesn't work with latest design support library – Rajesh.k Jan 08 '18 at 06:41
  • Doesn't work for me, too. I'm using support lib 27.0.3. – Vinh Nguyen Jan 21 '18 at 14:24
  • Doesn't work anymore - anyone got a solution? – Muppet Feb 20 '18 at 00:52
  • @ChrisJeon no, it's not. Google wants to maintain consistency by not allowing developers to change the behavior in every other app. – Saket Mar 16 '18 at 06:17
  • Working for support library 27.1.1. Thank you! – Idris Bohra Jun 24 '18 at 11:40
  • 3
    In support library 28.0.0-alpha3 `setShiftingMode` is renamed to `setShifting` – Eric B. Jul 17 '18 at 05:54
  • 3
    Why the cannot just keep a property for this in xml to acheive this – Pankaj Aug 12 '18 at 10:49
  • In support library 28.0.0 this method is no more. Google wants us to live with this shitty expanding icons here. – Sravan Nov 20 '18 at 18:50
  • But It's Still Shifting minor. – Prince Dholakiya Nov 23 '18 at 04:52
  • 1
    it still shifts up a little. as for google, they don't animate it in youtube app. so that's some consistency there for you. – Lassi Kinnunen Feb 08 '19 at 05:32
  • @LassiKinnunen, in my case it is also shifting up a little, have u got any solutions for that. – Ananta Prasad Mar 11 '19 at 07:18
  • @LAnantaPrasad Sorry, I ended up just doing my bottom navigation by doing a layout for it and then doing the little bit of code necessary to change color of the active one and adding the onclicks on it. All and all it took way less time and didn't add extra dependencies and it works like I want now. All and all it's not really that much more code and it's easier to change to any animation or behavior now. Had I gone straight to that I would have saved several hours of time. as I had the onNavigationItemSelected I just made the custom code call that, so the change was fairly minimal – Lassi Kinnunen Mar 13 '19 at 04:30
  • 1
    Do add the field - app:labelVisibilityMode="unlabeled" in bottom navigation view in the XML file. This works with the new design library. – Shivani Rastogi Apr 09 '19 at 06:13
  • 2
    Unfortunately it doesn't work with import ```com.google.android.material.bottomnavigation.BottomNavigationView;``` – Yvgen Jul 22 '19 at 09:29