28

As I liked the design from BottomNavigationView I decided to implement a new Menu for my App with it, instead of just using simple buttons.

I took this post as a guideline.

According to BottomNavigationView's documentation, its purpose is to

provide quick navigation between top-level views of an app. It is primarily designed for use on mobile.

In my case, I just want each MenuItem to launch an activity, but by default there is always one MenuItem selected:

enter image description here

I tried to set the color to white with:

app:itemIconTint="@color/white"
app:itemTextColor="@color/white"

Still, visibly selected MenuItem is different from others (Title size bigger), which is still bothering me:

enter image description here

I came with the idea to place a hidden MenuItem to select like:

<item
android:id="@+id/uncheckedItem"
android:title="" />

and make its view GONE:

 bottomNavigationView.getMenu().findItem(R.id.uncheckedItem).setChecked(true);
 bottomNavigationView.findViewById(R.id.uncheckedItem).setVisibility(View.GONE);

This makes all MenuItems unchecked, but by default BottomNavigationView is hidding Titles, as it has more than 3 MenuItems to display, even if the fourth MenuItem is settle to GONE:

enter image description here

So my question remains, is there away/hack to unselect all MenuItems and keep its titles being displayed?

Community
  • 1
  • 1
Victor Oliveira
  • 3,293
  • 7
  • 47
  • 77
  • You're doing it right but need to add a property to your `BottomNavigationView`. See my answer. – Dan Bray Jun 12 '19 at 16:18
  • please try my answer by setting the visibility of "uncheckedItem" to false. in the xml menu rather than doing it programmatically – Amrutha Saj Mar 26 '20 at 07:29

11 Answers11

43
mNavigationBottom.getMenu().setGroupCheckable(0, false, true);
Rony Tesler
  • 1,207
  • 15
  • 25
  • 13
    This does deselect all items without any side effects. However, you must later put "mNavigationBottom.getMenu().setGroupCheckable(0, true, true);" somewhere to allow the items to be shown as selected again. – MacD Apr 24 '18 at 15:30
  • This removes the selected menu item's color, but the title is still bold for some reason.. – nhcodes Jun 26 '23 at 20:55
31

To unselect all items I have create this extension:

fun BottomNavigationView.uncheckAllItems() {
    menu.setGroupCheckable(0, true, false)
    for (i in 0 until menu.size()) {
        menu.getItem(i).isChecked = false
    }
    menu.setGroupCheckable(0, true, true)
}

menu.setGroupCheckable(0, true, false) make it possible. The third parameter made the menu not exclusive and then within the loop you change the checked status. To finish set the menu to exclusive again.

Here the doc

ilbose
  • 747
  • 13
  • 21
paolo
  • 340
  • 3
  • 7
5

Thanks for your idea. I have implement it in my lib. I have a better way do it by reflect. So it won't show space.

If you have interest. Click here : https://github.com/ittianyu/BottomNavigationViewEx

private void initBottomViewAndLoadFragments(final BottomNavigationViewEx bnve) {
    bnve.enableAnimation(false);
    bnve.enableShiftingMode(false);
    bnve.enableItemShiftingMode(false);

    // use the unchecked color for first item
    bnve.setIconTintList(0, getResources().getColorStateList(R.color.bnv_unchecked_black));
    bnve.setTextTintList(0, getResources().getColorStateList(R.color.bnv_unchecked_black));

    bnve.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {

        private boolean firstClick = true;
        private int lastItemId = -1;

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            // restore the color when click
            if (firstClick) {
                firstClick = false;
                bnve.setIconTintList(0, getResources().getColorStateList(R.color.selector_bnv));
                bnve.setTextTintList(0, getResources().getColorStateList(R.color.selector_bnv));
            }

            if (firstClick || lastItemId == -1 || lastItemId != item.getItemId()) {
                lastItemId = item.getItemId();
            } else {
                return false;
            }

            // do stuff
            return fillContent(item.getItemId());
        }
    });
}

-- res/color/selector_bnv.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/bnv_checked_white" android:state_checked="true" />
    <item android:color="@color/bnv_unchecked_black" />
</selector>

-- res/values/colors.xml

<color name="bnv_checked_white">@android:color/white</color>
<color name="bnv_unchecked_black">@android:color/black</color>
oxied
  • 1,773
  • 19
  • 14
ittianyu
  • 509
  • 4
  • 7
  • 1
    Good work! Please, write how to make first time unchecked. Didn't find code for this in your repo. – oxied Dec 06 '17 at 13:11
  • UPDATE: found in his repo: https://github.com/ittianyu/BottomNavigationViewEx/blob/master/app/src/main/java/com/ittianyu/bottomnavigationviewexsample/features/style/StyleActivity.java#L152 – oxied Dec 06 '17 at 15:31
  • This should be the right answer. Awesome lib, thanks for the sample. – Neonigma May 12 '18 at 14:24
4

Your solution seems change the space between items

There is my solution :

"Just set color of clicked same as color of un-clicked."

for example:

private void changeMenuItemCheckedStateColor(BottomNavigationView bottomNavigationView, String checkedColorHex, String uncheckedColorHex) {
    int checkedColor = Color.parseColor(checkedColorHex);
    int uncheckedColor = Color.parseColor(uncheckedColorHex);

    int[][] states = new int[][] {
            new int[] {-android.R.attr.state_checked}, // unchecked
            new int[] {android.R.attr.state_checked}, // checked

    };

    int[] colors = new int[] {
            uncheckedColor,
            checkedColor
    };

    ColorStateList colorStateList = new ColorStateList(states, colors);

    bottomNavigationView.setItemTextColor(colorStateList);
    bottomNavigationView.setItemIconTintList(colorStateList);

}

if you want to un-check all items, you can

changeMenuItemCheckedStateColor(mBottomNavigationView, "#999999", "#999999");

if you want to restore the color setting, you can

changeMenuItemCheckedStateColor(mBottomNavigationView, "FF743A", "999999");
楊舜淼
  • 73
  • 6
3

I found my own solution merging my progress with this post.

Steps:

  1. Update proguard-rules.pro and sync build
  2. Create Helper to disable BottomNavigationView Shift Mode
  3. Create an Item to hide on Menu.xml
  4. Inflate BottomNavigationView
  5. Set Item to be hidden as Checked GONE
  6. Use Helper to disable Shifting Mode

Output:

enter image description here

Working code:

proguard-rules.pro:

-keepclassmembers class android.support.design.internal.BottomNavigationMenuView {
    boolean mShiftingMode;
}

BottomNavigationShiftHelper.java:

public class BottomNavigationShiftHelper {

    private final static String TAG = "DEBUG_BOTTOM_NAV_UTIL";

    public static void disableShiftMode(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.d(TAG, "Unable to get shift mode field");
        } catch (IllegalAccessException e) {
            Log.d(TAG, "Unable to change value of shift mode");
        }
    }
}

Activity Sample.java:

 private void loadNavigationBar() {
        BottomNavigationView bottomNavigationView = (BottomNavigationView)
                findViewById(R.id.navigation_bar);

        bottomNavigationView.getMenu().findItem(R.id.uncheckedItem).setChecked(true);
        bottomNavigationView.findViewById(R.id.uncheckedItem).setVisibility(View.GONE);

        BottomNavigationViewUtils.disableShiftMode(bottomNavigationView);

        bottomNavigationView.setOnNavigationItemSelectedListener(
                new BottomNavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                        switch (item.getItemId()) {
                            case R.id.newList:
                                //Do The Math
                                break;
                            case R.id.loadList:
                                //Do The Math
                                break;
                            case R.id.settings:
                                //Do The Math
                                break;
                        }
                        return false;
                    }
                });
    }

BottomNavigationMenu.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/newList"
        android:enabled="true"
        android:icon="@drawable/new_list"
        android:title="@string/common.button.list.new"
        app:showAsAction="withText" />
    <item
        android:id="@+id/loadList"
        android:enabled="true"
        android:icon="@drawable/load"
        android:title="@string/common.button.list.load"
        app:showAsAction="withText" />
    <item
        android:id="@+id/settings"
        android:enabled="true"
        android:icon="@drawable/settings"
        android:title="@string/common.label.settings"
        app:showAsAction="withText" />
    <item
        android:id="@+id/uncheckedItem"
        android:title="" />
</menu>

BottomNavigationComponent (inside Activity.xml):

<android.support.design.widget.BottomNavigationView
    android:id="@+id/navigation_bar"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    app:itemIconTint="@color/white"
    app:itemTextColor="@color/white"
    android:background="@drawable/BottomNavigationMenu.xml"
    app:menu="@menu/supercart_bottom_navigation" />
Community
  • 1
  • 1
Victor Oliveira
  • 3,293
  • 7
  • 47
  • 77
  • it works, but now my bottom navigation view didn't take all width...can you please assist – Reprator Jun 01 '17 at 11:13
  • i got the following issue, https://github.com/ittianyu/BottomNavigationViewEx/issues/25 – Reprator Jun 01 '17 at 12:44
  • 3
    I have 5 items and if I add a empty item I get this error: Maximum number of items supported by BottomNavigationView is 5 – Kusan Sep 12 '17 at 09:17
  • @Kusan, check out my answer below. Hopefully it works for you https://stackoverflow.com/a/46491753/1236327 – tim.paetz Sep 29 '17 at 15:07
  • I wasted too much time on adjusting this menu, I found a better way (not ideal), I now use different menu resources for each user class – Marc Jan 08 '18 at 04:51
2

Try this, it worked for me

 <item
    android:id="@+id/uncheckedItem"
    android:visible="false"
    android:title="" />

and set

bottomNavigationView.getMenu().findItem(R.id.uncheckedItem).setChecked(true);

enter image description here

you get all menu item view as unselected; since the selection is given for uncheckedItem which is invisible

Hope it helped you.

Amrutha Saj
  • 1,408
  • 16
  • 35
  • 1
    Only works if you are using less than the maximum of 5 items in a bottom navigation menu so you need to use 1 of the menu items as the invisible one (android:visible="false") – BENN1TH Mar 30 '20 at 08:27
1

There is an exception in accepted answer which we set maximum item we cant implement that code.So I got a code that is more simple than accepted code and it's also working with maximum item.

I referred from here Custom TextSize of BottomNavigationView support android

In your dimen.xml you can put:

<dimen name="design_bottom_navigation_text_size" tools:override="true">10sp</dimen>
<dimen name="design_bottom_navigation_active_text_size" tools:override="true">10sp</dimen>

Doing this you are overriding the default value of dimen that the internal classes of BottomNavigationView use. So be carreful.

Set this code in your onCreate method where the bottom navigation view initialized

mNavigationBottom.getMenu().setGroupCheckable(0, false, true);

and Last set your bottom navigation bar like this :

 <com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/nav_view"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:layout_gravity="bottom"
    android:background="?android:attr/windowBackground"
    android:fitsSystemWindows="true"
    app:labelVisibilityMode="labeled"
    app:layout_anchorGravity="fill"
    app:menu="@menu/bottom_nav_menu" />

In this set your app:labelVisibilityMode to labeled

SoloWolf93
  • 1,090
  • 11
  • 25
0

This is the same as the accepted answer except I have changed two lines of code marked below. When looping through the BottomNavigationItemViews, I set checked to false always and I also set checkable to false. This prevents the Menu Items from changing size. You still need this proguard rule:

      -keepclassmembers class android.support.design.internal.BottomNavigationMenuView {
         boolean mShiftingMode;
      }

Updated code:

    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);
                item.setChecked(false); // <--- This line changed
                item.setCheckable(false);  // <-- This line was added
            }
        }
        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");
        }

   }
tim.paetz
  • 2,635
  • 20
  • 23
0

The best answer mNavigationBottom.getMenu().setGroupCheckable(0, false, true) did not work for me. It still showed the first item as being selected.

What did work for me is to have an invisible item and to select that, exactly like you did in the question.

Add app:labelVisibilityMode="labeled" to your BottomNavigationView so that all items remain in view with their titles.

Dan Bray
  • 7,242
  • 3
  • 52
  • 70
0
    if(isDarkMode(context)) {
        mBotNavView.setItemIconTintList(ColorStateList.valueOf(Color.parseColor("#FFFFFF")));
    } else {
        mBotNavView.setItemIconTintList(ColorStateList.valueOf(Color.parseColor("#000000")));
    }

    public boolean isDarkMode(Context context) {
        int nightModeFlags = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
        return darkMode = (nightModeFlags == Configuration.UI_MODE_NIGHT_YES);
    }
0

Another pure XML solution to this is to just add some styles and match them for both active and inactive states and then define them to your BottomNavigationView:

In your XML file that contains your bottomnavigation view:

            <com.google.android.material.bottomnavigation.BottomNavigationView

            app:itemTextAppearanceActive="@style/BottomNavigationView.Active"
            app:itemTextAppearanceInactive="@style/BottomNavigationView.Inactive"

Then in your style.xml file:

<style name="BottomNavigationView.Inactive" parent="@style/TextAppearance.AppCompat.Caption">
            <item name="android:textSize">10sp</item>
            <item name="android:textStyle">bold</item>
    </style>
    
<style name="BottomNavigationView.Active" parent="@style/TextAppearance.AppCompat.Caption">
            <item name="android:textSize">10sp</item>
    </style>

Match both the active and inactive states of the buttons and they'll look the same.

Will
  • 185
  • 1
  • 8