Set selected item in Android BottomNavigationView
Asked Answered
R

24

171

I am using the new android.support.design.widget.BottomNavigationView from the support library. How can I set the current selection from code? I realized, that the selection is changed back to the first item, after rotating the screen. Of course it would also help, if someone could tell me, how to "save" the current state of the BottomNavigationView in the onPause function and how to restore it in onResume.

Thanks!

Recurrence answered 23/10, 2016 at 11:17 Comment(1)
I've just had a look at the source and I have the same question.. It doesn't look like there is a method to set the selected item, and BottomNavigationView doesn't do any internal saving of state. Probably expect this to be included in a future update. Duplicate (with some more info) here: #40237286Amphigory
R
4

Seems to be fixed in SupportLibrary 25.1.0 :) Edit: It seems to be fixed, that the state of the selection is saved, when rotating the screen.

Recurrence answered 20/12, 2016 at 15:23 Comment(1)
I just updated to 25.1.1. The problem is still there.Mayberry
E
230

From API 25.3.0 it was introduced the method setSelectedItemId(int id) which lets you mark an item as selected as if it was tapped.

From docs:

Set the selected menu item ID. This behaves the same as tapping on an item.

Code example:

BottomNavigationView bottomNavigationView;
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(myNavigationItemListener);
bottomNavigationView.setSelectedItemId(R.id.my_menu_item_id);

IMPORTANT

You MUST have already added all items to the menu (in case you do this programmatically) and set the Listener before calling setSelectedItemId(I believe you want the code in your listener to run when you call this method). If you call setSelectedItemId before adding the menu items and setting the listener nothing will happen.

Embree answered 30/3, 2017 at 12:9 Comment(9)
This does NOT WORK, it only updates the tab itself, it doesnt trigger a setOnNavigationItemSelectedListener eventSomniloquy
If you haven't defined the behavior in the onTabSelected obviously it won't do anything. Read the docs -> Set the selected menu item ID. This behaves the same as tapping on an item. It's from Android DevelopersEmbree
I highly advise you to debug and review what you are doing wrong. I have already implemented three BottomNavigationView Applications and never had a problem with this method.Embree
@Embree When I add this to my fragment, application got hang while entering to that fragmentDelanadelancey
@SubinBabu that is probably due to your implementation in OnNavigationItemSelectedListener. The docs clearly say that "setSelectedItemId()` behaves the same as tapping on an item. Your application also hangs when you tap on them?Embree
@Embree its ANR and close the application. Would you help to make the bottom navigation view tab selection from another fragment of same activityDelanadelancey
sure, use the following method in your fragment: ((MyActivity)getActivity()).myBottomNavigationView.seSelectedItemId(R.id.itemId); Let me know if it fixed your problemEmbree
In my case wasn't working, because I was creating navigationMenu programatically. During construction the click didn't work. After all items added to menu, calling setSelectedItemId() worked.Converted
Won't trigger for the first item in BottomNavigationView but works for the rest. This is expected behavior though. Because the first item is selected by default at start-up and setSelectedItemId(itemId) will trigger onNavigationItemReselected instead of onNavigationItemSelectedGremial
B
78

To programmatically click on the BottomNavigationBar item you need use:

View view = bottomNavigationView.findViewById(R.id.menu_action_item);
view.performClick();

This arranges all the items with their labels correctly.

Bedfordshire answered 30/1, 2017 at 23:40 Comment(1)
This is an hack regarding the problem. You have methods from the library itself which will let you perform what you need. If you can't then it's because you are doing it wrongEmbree
D
51

For those, who still use SupportLibrary < 25.3.0

I'm not sure whether this is a complete answer to this question, but my problem was very similar - I had to process back button press and bring user to previous tab where he was. So, maybe my solution will be useful for somebody:

private void updateNavigationBarState(int actionId){
    Menu menu = bottomNavigationView.getMenu();

    for (int i = 0, size = menu.size(); i < size; i++) {
        MenuItem item = menu.getItem(i);
        item.setChecked(item.getItemId() == actionId);
    }
}

Please, keep in mind that if user press other navigation tab BottomNavigationView won't clear currently selected item, so you need to call this method in your onNavigationItemSelected after processing of navigation action:

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
        case R.id.some_id_1:
            // process action
            break;
        case R.id.some_id_2:
            // process action
            break;
        ...
        default:
            return false;
    }

    updateNavigationBarState(item.getItemId());

    return true;
}

Regarding the saving of instance state I think you could play with same action id of navigation view and find suitable solution.

Decarbonate answered 21/11, 2016 at 13:10 Comment(4)
Doesn´t Menu menu = bottomNavigationView.getMenu(); return a copy of the menu, so the bottomNavigationView object is not affected?Killion
@dan According to source it returns class attribute mMenu, not the copy android.googlesource.com/platform/frameworks/support/+/master/…Decarbonate
You don't need to uncheck other items, at least on 26.1.0/27.1.0Sunshade
don't do uncheck, it will highlight themGonococcus
E
46
bottomNavigationView.setSelectedItemId(R.id.action_item1);

where action_item1 is menu item ID.

Excretory answered 14/12, 2018 at 11:14 Comment(4)
This works perfectly to call setOnNavigationItemSelectedListener .Trainee
why did I have to scroll all the way to find this. no other answer is native to apis or is way too complicatedPalliasse
There is still no way to do this if the bottomNavigationView uses a dynamically built menu. My MenuItems all have an id of 0.Friesland
The best answer for me. It should be the accepted one.Farinose
B
19

Use this to set selected bottom navigation menu item by menu id

MenuItem item = mBottomNavView.getMenu().findItem(menu_id);
item.setChecked(true);
Brasca answered 23/3, 2020 at 5:23 Comment(2)
Use this to avoid navigating to that item if using Jetpack NavigationMungo
Valeu! Funciona demais. Use findItem(R.id.seuMenuId); se tiver vários itens, use MenuItem nomeItem01 =, MenuItem nomeItem02 = ...nomeItem01.setChecked(true); nomeItem02.setChecked(true);Wyon
A
18

use

        bottomNavigationView.getMenu().getItem(POSITION).setChecked(true);
Alfrediaalfredo answered 3/1, 2020 at 18:14 Comment(3)
getItem(0) will eventually take him to the 1st tab only and not retain the current tab.Hilton
It ain't working.Shatterproof
This is the only way I could do it when building the menu programatically.Friesland
N
9
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

   bottomNavigationView.setOnNavigationItemSelectedListener(this);
   Menu menu = bottomNavigationView.getMenu();
   this.onNavigationItemSelected(menu.findItem(R.id.action_favorites));
}
Nuristan answered 24/11, 2016 at 19:19 Comment(0)
S
9

It is now possible since 25.3.0 version to call setSelectedItemId() \o/

Straightedge answered 28/3, 2017 at 8:14 Comment(0)
D
8

Add android:enabled="true" to BottomNavigationMenu Items.

And then set bottomNavigationView.setOnNavigationItemSelectedListener(mListener) and set it as selected by doing bottomNavigationView.selectedItemId = R.id.your_menu_id

Dhoti answered 23/6, 2018 at 17:48 Comment(0)
M
7

You can try the performClick method :

View view = bottomNavigationView.findViewById(R.id.YOUR_ACTION);
view.performClick();

Edit

From API 25.3.0 it was introduced the method setSelectedItemId(int id) which lets you mark an item as selected as if it was tapped.

Minneapolis answered 11/10, 2018 at 1:21 Comment(0)
B
6
navigationView.getMenu().findItem(R.id.navigation_id).setChecked(true);
Breechloader answered 1/4, 2021 at 15:50 Comment(1)
Was looking for this as using the setSelectedItemId() triggers the navigationFreddiefreddy
A
5

Above API 25 you can use setSelectedItemId(menu_item_id) but under API 25 you must do differently, user Menu to get handle and then setChecked to Checked specific item

Abshire answered 5/4, 2018 at 13:44 Comment(0)
C
4

This will probably be added in coming updates. But in the meantime, to accomplish this you can use reflection.

Create a custom view extending from BottomNavigationView and access some of its fields.

public class SelectableBottomNavigationView extends BottomNavigationView {

    public SelectableBottomNavigationView(Context context) {
        super(context);
    }

    public SelectableBottomNavigationView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SelectableBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setSelected(int index) {
        try {
            Field f = BottomNavigationView.class.getDeclaredField("mMenuView");
            f.setAccessible(true);
            BottomNavigationMenuView menuView = (BottomNavigationMenuView) f.get(this);

            try {
                Method method = menuView.getClass().getDeclaredMethod("activateNewButton", Integer.TYPE);
                method.setAccessible(true);
                method.invoke(menuView, index);
            } catch (SecurityException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

}

And then use it in your xml layout file.

<com.your.app.SelectableBottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:itemBackground="@color/primary"
        app:itemIconTint="@drawable/nav_item_color_state"
        app:itemTextColor="@drawable/nav_item_color_state"
        app:menu="@menu/bottom_navigation_menu"/>
Cherrylchersonese answered 28/10, 2016 at 23:54 Comment(2)
Reflection is a bad idea!Circumlocution
performClick is better than reflection, imho, in this case.Talbott
P
4

I you don't want to modify your code. If so, I recommended you to try BottomNavigationViewEx

You just need replace call a method setCurrentItem(index); and getCurrentItem()

Click here to view the image

Pylos answered 13/11, 2016 at 3:40 Comment(0)
F
4

Just adding another way to perform a selection programatically - this is probably what was the intention in the first place or maybe this was added later on.

Menu bottomNavigationMenu = myBottomNavigationMenu.getMenu();
bottomNavigationMenu.performIdentifierAction(selected_menu_item_id, 0);

The performIdentifierAction takes a Menu item id and a flag.

See the documentation for more info.

Foreskin answered 20/11, 2016 at 21:41 Comment(1)
This seems to perform the action belonging to the menu item, but doesn't cause the menu item to appear selected. I thought the latter was what the OP wanted, but I'm not positive.Cyn
R
4

Seems to be fixed in SupportLibrary 25.1.0 :) Edit: It seems to be fixed, that the state of the selection is saved, when rotating the screen.

Recurrence answered 20/12, 2016 at 15:23 Comment(1)
I just updated to 25.1.1. The problem is still there.Mayberry
L
2

I made a bug to Google about the fact that there's no reliable way to select the page on a BottomNavigationView: https://code.google.com/p/android/issues/detail?id=233697

NavigationView apparently had a similar issue, which they fixed by adding a new setCheckedItem() method.

Lulu answered 8/2, 2017 at 6:26 Comment(0)
M
2

I hope this helps

//Setting default selected menu item and fragment
        bottomNavigationView.setSelectedItemId(R.id.action_home);
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.fragment_container, new HomeFragment()).commit();

It is more of determining the default fragment loaded at the same time with the corresponding bottom navigation menu item. You can include the same in your OnResume callbacks

Machinist answered 8/2, 2020 at 11:45 Comment(0)
C
1

To change the Tab, this code works!

    activity?.supportFragmentManager?.beginTransaction().also { fragmentTransaction ->
        fragmentTransaction?.replace(R.id.base_frame, YourFragment())?.commit()
    }

    val bottomNavigationView: BottomNavigationView = activity?.findViewById(R.id.bottomNavigationView) as BottomNavigationView
    bottomNavigationView.menu.findItem(R.id.navigation_item).isChecked = true
Crybaby answered 27/2, 2022 at 5:47 Comment(0)
H
1

in Kotlin: bottomNavigationView.menu.getItem(POSITION).isChecked = true

Honeycomb answered 12/3, 2023 at 13:3 Comment(0)
M
0

Reflection is bad idea.

Head to this gist. There is a method that performs the selection but also invokes the callback:

  @CallSuper
    public void setSelectedItem(int position) {
        if (position >= getMenu().size() || position < 0) return;

        View menuItemView = getMenuItemView(position);
        if (menuItemView == null) return;
        MenuItemImpl itemData = ((MenuView.ItemView) menuItemView).getItemData();


        itemData.setChecked(true);

        boolean previousHapticFeedbackEnabled = menuItemView.isHapticFeedbackEnabled();
        menuItemView.setSoundEffectsEnabled(false);
        menuItemView.setHapticFeedbackEnabled(false); //avoid hearing click sounds, disable haptic and restore settings later of that view
        menuItemView.performClick();
        menuItemView.setHapticFeedbackEnabled(previousHapticFeedbackEnabled);
        menuItemView.setSoundEffectsEnabled(true);


        mLastSelection = position;

    }
Monanthous answered 15/11, 2016 at 22:50 Comment(1)
I use callOnClick() instead of performClick(), this way I don't need to disable sound, which didn't work with performClick() anyway.Dialogism
F
0
private void setSelectedItem(int actionId) {
    Menu menu = viewBottom.getMenu();
    for (int i = 0, size = menu.size(); i < size; i++) {
        MenuItem menuItem = menu.getItem(i);
        ((MenuItemImpl) menuItem).setExclusiveCheckable(false);
        menuItem.setChecked(menuItem.getItemId() == actionId);
        ((MenuItemImpl) menuItem).setExclusiveCheckable(true);
    }
}

The only 'minus' of the solution is using MenuItemImpl, which is 'internal' to library (though public).

Feebleminded answered 4/1, 2017 at 5:49 Comment(0)
S
0

IF YOU NEED TO DYNAMICALLY PASS FRAGMENT ARGUMENTS DO THIS

There are plenty of (mostly repeated or outdated) answers here but none of them handles a very common need: dynamically passing different arguments to the Fragment loaded into a tab.

You can't dynamically pass different arguments to the loaded Fragment by using setSelectedItemId(R.id.second_tab), which ends up calling the static OnNavigationItemSelectedListener. To overcome this limitation I've ended up doing this in my MainActivity that contains the tabs:

fun loadArticleTab(articleId: String) {
    bottomNavigationView.menu.findItem(R.id.tab_article).isChecked = true // use setChecked() in Java
    supportFragmentManager
        .beginTransaction()
        .replace(R.id.main_fragment_container, ArticleFragment.newInstance(articleId))
        .commit()
}

The ArticleFragment.newInstance() method is implemented as usual:

private const val ARG_ARTICLE_ID = "ARG_ARTICLE_ID"

class ArticleFragment : Fragment() {

    companion object {
        /**
         * @return An [ArticleFragment] that shows the article with the given ID.
         */
        fun newInstance(articleId: String): ArticleFragment {
            val args = Bundle()
            args.putString(ARG_ARTICLE_ID, day)
            val fragment = ArticleFragment()
            fragment.arguments = args
            return fragment
        }
    }

}
Sestertium answered 16/8, 2019 at 14:53 Comment(0)
B
0

This method work for me.

private fun selectBottomNavigationViewMenuItem(bottomNavigationView : BottomNavigationView,@IdRes menuItemId: Int) {
            bottomNavigationView.setOnNavigationItemSelectedListener(null)
            bottomNavigationView.selectedItemId = menuItemId
            bottomNavigationView.setOnNavigationItemSelectedListener(this)
        }

Example

 override fun onBackPressed() {
        replaceFragment(HomeFragment())
        selectBottomNavigationViewMenuItem(navView, R.id.navigation_home)
    }



private fun replaceFragment(fragment: Fragment) {
        val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
        transaction.replace(R.id.frame_container, fragment)
        transaction.commit()
    }
Barta answered 14/7, 2020 at 10:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.