在Android BottomNavigationView中设置所选项目

时间:2023-01-19 08:58:19

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.

我正在使用支持库中新的android.support.design.widget.BottomNavigationView。如何从代码中设置当前选择?我意识到,在旋转屏幕后,选择会更改回第一项。当然,如果有人能告诉我,如何在onPause函数中“保存”BottomNavigationView的当前状态以及如何在onResume中恢复它也会有所帮助。

Thanks!

谢谢!

14 个解决方案

#1


64  

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

从API 25.3.0开始,引入了方法setSelectedItemId(int id),让你将项目标记为已选中,就像它被点击一样。

From docs:

来自docs:

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

设置所选的菜单项ID。这与点击项目的行为相同。

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 and set the Listener before calling setSelectedItemId so that the BottomNavigationView can run the corresponding code's behaviour. If you call setSelectedItemId before adding the menu items and setting the listener nothing will happen.

您必须已经将所有项目添加到菜单中并在调用setSelectedItemId之前设置监听器,以便BottomNavigationView可以运行相应代码的行为。如果在添加菜单项之前调用setSelectedItemId并设置监听器,则不会发生任何事情。

#2


24  

To programmatically click on the BottomNavigationBar item you need use:

要以编程方式单击您需要使用的BottomNavigationBar项:

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

This arranges all the items with their labels correctly.

这将正确排列所有项目及其标签。

#3


23  

For those, who still use SupportLibrary < 25.3.0

对于那些仍然使用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:

请记住,如果用户按下其他导航选项卡,则BottomNavigationView将不会清除当前选定的项目,因此您需要在处理导航操作后在onNavigationItemSelected中调用此方法:

@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.

关于保存实例状态,我认为您可以使用导航视图的相同操作ID并找到合适的解决方案。

#4


5  

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

现在可以从25.3.0版本调用setSelectedItemId()\ o /

#5


3  

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.

创建从BottomNavigationView扩展的自定义视图并访问其某些字段。

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.

然后在xml布局文件中使用它。

<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"/>

#6


3  

Try my solution from https://*.com/a/40778162/2571249

从https://*.com/a/40778162/2571249尝试我的解决方案

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

   bottomNavigationView.setOnNavigationItemSelectedListener(this);
   Menu menu = bottomNavigationView.getMenu();
   this.onNavigationItemSelected(menu.findItem(R.id.action_favorites));
}

#7


2  

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.

似乎在SupportLibrary 25.1.0中修复了:)编辑:似乎是固定的,在旋转屏幕时保存选择的状态。

#8


2  

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

在API 25上面你可以使用setSelectedItemId(menu_item_id),但是在API 25下你必须做不同的事情,用户菜单来获取句柄然后setChecked to Checked特定项目

#9


1  

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

我不想修改你的代码。如果是这样,我建议你尝试BottomNavigationViewEx。

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

你只需要替换调用方法setCurrentItem(index);和getCurrentItem()。

在Android BottomNavigationView中设置所选项目

#10


1  

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.

performIdentifierAction采用菜单项ID和标志。

See the documentation for more info.

有关详细信息,请参阅文档。

#11


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).

解决方案的唯一“减号”是使用MenuItemImpl,它是库的“内部”(尽管是公共的)。

#12


0  

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

我向谷歌提出了一个错误,即在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.

NavigationView显然有一个类似的问题,他们通过添加一个新的setCheckedItem()方法来修复它。

#13


0  

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

将android:enabled =“true”添加到BottomNavigationMenu项目。

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

然后设置bottomNavigationView.setOnNavigationItemSelectedListener(mListener)并通过执行bottomNavigationView.selectedItemId = R.id.your_menu_id将其设置为选中

#14


-1  

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;

    }

#1


64  

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

从API 25.3.0开始,引入了方法setSelectedItemId(int id),让你将项目标记为已选中,就像它被点击一样。

From docs:

来自docs:

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

设置所选的菜单项ID。这与点击项目的行为相同。

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 and set the Listener before calling setSelectedItemId so that the BottomNavigationView can run the corresponding code's behaviour. If you call setSelectedItemId before adding the menu items and setting the listener nothing will happen.

您必须已经将所有项目添加到菜单中并在调用setSelectedItemId之前设置监听器,以便BottomNavigationView可以运行相应代码的行为。如果在添加菜单项之前调用setSelectedItemId并设置监听器,则不会发生任何事情。

#2


24  

To programmatically click on the BottomNavigationBar item you need use:

要以编程方式单击您需要使用的BottomNavigationBar项:

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

This arranges all the items with their labels correctly.

这将正确排列所有项目及其标签。

#3


23  

For those, who still use SupportLibrary < 25.3.0

对于那些仍然使用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:

请记住,如果用户按下其他导航选项卡,则BottomNavigationView将不会清除当前选定的项目,因此您需要在处理导航操作后在onNavigationItemSelected中调用此方法:

@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.

关于保存实例状态,我认为您可以使用导航视图的相同操作ID并找到合适的解决方案。

#4


5  

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

现在可以从25.3.0版本调用setSelectedItemId()\ o /

#5


3  

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.

创建从BottomNavigationView扩展的自定义视图并访问其某些字段。

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.

然后在xml布局文件中使用它。

<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"/>

#6


3  

Try my solution from https://*.com/a/40778162/2571249

从https://*.com/a/40778162/2571249尝试我的解决方案

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

   bottomNavigationView.setOnNavigationItemSelectedListener(this);
   Menu menu = bottomNavigationView.getMenu();
   this.onNavigationItemSelected(menu.findItem(R.id.action_favorites));
}

#7


2  

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.

似乎在SupportLibrary 25.1.0中修复了:)编辑:似乎是固定的,在旋转屏幕时保存选择的状态。

#8


2  

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

在API 25上面你可以使用setSelectedItemId(menu_item_id),但是在API 25下你必须做不同的事情,用户菜单来获取句柄然后setChecked to Checked特定项目

#9


1  

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

我不想修改你的代码。如果是这样,我建议你尝试BottomNavigationViewEx。

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

你只需要替换调用方法setCurrentItem(index);和getCurrentItem()。

在Android BottomNavigationView中设置所选项目

#10


1  

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.

performIdentifierAction采用菜单项ID和标志。

See the documentation for more info.

有关详细信息,请参阅文档。

#11


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).

解决方案的唯一“减号”是使用MenuItemImpl,它是库的“内部”(尽管是公共的)。

#12


0  

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

我向谷歌提出了一个错误,即在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.

NavigationView显然有一个类似的问题,他们通过添加一个新的setCheckedItem()方法来修复它。

#13


0  

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

将android:enabled =“true”添加到BottomNavigationMenu项目。

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

然后设置bottomNavigationView.setOnNavigationItemSelectedListener(mListener)并通过执行bottomNavigationView.selectedItemId = R.id.your_menu_id将其设置为选中

#14


-1  

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;

    }