在Android BottomNavigationView中设置选定的项目


122

我正在使用android.support.design.widget.BottomNavigationView支持库中的新功能。如何通过代码设置当前选择?我意识到,旋转屏幕后,选择项又变回了第一项。当然,它也将有所帮助,如果有人能告诉我,如何“拯救”的当前状态BottomNavigationViewonPause功能,以及如何将其在恢复onResume

谢谢!


1
我刚刚看过源代码,也有同样的问题。看起来好像没有一种方法可以设置所选项目,并且BottomNavigationView不做任何内部状态保存。可能希望将此内容包含在将来的更新中。复制(使用更多的一些信息)位置:stackoverflow.com/questions/40236786/...
蒂姆Malseed

Answers:


170

从API 25.3.0开始,引入了一种方法setSelectedItemId(int id),该方法使您可以将某个项目标记为已选中,就好像它已被点击一样。

从文档:

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

代码示例:

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

重要

必须已经将所有项目添加到菜单中(以防您以编程方式执行此操作)并在调用setSelectedItemId之前设置侦听器(我相信您希望在调用此方法时运行侦听器中的代码)。如果在添加菜单项和设置侦听器之前调用setSelectedItemId,将不会发生任何事情。


3
这无效,它仅更新选项卡本身,不会触发setOnNavigationItemSelectedListener事件
TjerkW

1
如果您尚未明确定义行为,onTabSelected则将不会执行任何操作。阅读文档->设置所选菜单项ID。这与点击项目的行为相同。来自Android开发人员
里卡多(Ricardo)

1
我强烈建议您调试和检查您做错了什么。我已经实现了三个BottomNavigationView应用程序,并且从未遇到过这种方法的问题。
里卡多

@Ricardo当我将其添加到片段中时,应用程序进入该片段时挂起
Subin Babu

1
就我而言,这是行不通的,因为我正在以编程方式创建navigationMenu。在构建过程中,单击不起作用。将所有项目添加到菜单后,调用setSelectedItemId()即可。
PedroRomão18年

53

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

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

这样可以正确排列所有带有标签的项目。


4
这是关于该问题的技巧。您可以从库本身获得方法,这些方法可以让您执行所需的操作。如果不能,那是因为您做错了
Ricardo

47

对于那些仍使用SupportLibrary <25.3.0的用户

我不确定这是否是此问题的完整答案,但是我的问题非常相似-我必须处理back按钮按下操作,并将用户带到他所在的上一个选项卡。因此,也许我的解决方案对某人有用:

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);
    }
}

请记住,如果用户按下其他导航选项卡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;
}

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


是否不Menu menu = bottomNavigationView.getMenu();返回菜单的副本,因此bottomNavigationView对象不受影响?
最白目

1
@dan根据消息来源,它返回类属性mMenu,而不是副本android.googlesource.com/platform/frameworks/support/+/master/…–
Viacheslav

6
至少在26.1.0 / 27.1.0上,您无需取消选中其他项目
Jemshit Iskenderov,

不要取消勾选,它将突出显示它们
djdance

19
bottomNavigationView.setSelectedItemId(R.id.action_item1);

action_item1菜单项ID 在哪里。


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

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


5

添加android:enabled="true"到BottomNavigationMenu项。

然后设置bottomNavigationView.setOnNavigationItemSelectedListener(mListener)并设置为选中状态bottomNavigationView.selectedItemId = R.id.your_menu_id


3

这可能会在以后的更新中添加。但是,与此同时,您可以使用反射。

创建一个从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();
        }
    }

}

然后在您的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"/>

3
反思是一个坏主意!
Filipe Brito

在这种情况下,performClick比反射更好,恕我直言。
Lassi Kinnunen

3

只是添加了另一种以编程方式执行选择的方法-这可能首先是意图,或者可能是后来添加的。

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

performIdentifierAction需要Menu的产品ID和一个标志。

请参阅文档以获取更多信息。


这似乎执行了属于菜单项的操作,但并未使菜单项显示为选中状态。我以为后者是OP想要的,但我并不乐观。
LarsH '16

3

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


1
我刚刚更新到25.1.1。问题仍然存在。
习伟

3

在API 25以上,您可以使用setSelectedItemId(menu_item_id),但在API 25下,您必须执行其他操作,请使用用户Menu进行处理,然后将setChecked设置为Checked特定项目


3

使用此功能通过菜单ID设置选定的底部导航菜单项

MenuItem item = mBottomNavView.getMenu().findItem(menu_id);
item.setChecked(true);

2

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

您只需要替换调用方法setCurrentItem(index);getCurrentItem()

点击这里查看图片




1

        bottomNavigationView.getMenu().getItem(0).setChecked(true);

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);
    }
}

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


0

如果您需要动态通过片段参数,请执行此操作

这里有很多(大多数是重复的或过时的)答案,但是它们都不能满足非常普遍的需求:将不同的参数动态传递给加载到选项卡中的Fragment

您无法使用来将不同的参数动态传递给已加载的Fragment setSelectedItemId(R.id.second_tab),最终会调用static OnNavigationItemSelectedListener。为了克服此限制,我最终在MainActivity包含选项卡的我中这样做:

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()
}

ArticleFragment.newInstance()方法照常实现:

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
        }
    }

}

0

我希望这有帮助

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

更多的是确定与相应的底部导航菜单项同时加载的默认片段。您可以在OnResume回调中包含相同的内容


0

这种方法对我有用。

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

 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()
    }

-1

反思是个坏主意。

前往这个要点。有一个方法可以执行选择,还可以调用回调:

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

    }

我使用callOnClick()代替performClick(),这样我就不需要禁用声音了,performClick()无论如何这都行不通。
arekolek
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.