【问题标题】:Set selected item in Android BottomNavigationView在 Android BottomNavigationView 中设置所选项目
【发布时间】:2017-03-05 06:41:47
【问题描述】:

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

谢谢!

【问题讨论】:

  • 我刚刚看了一下源,我也有同样的问题。。看起来没有设置所选项目的方法,BottomNavigationView 没有任何内部保存状态。可能希望这将包含在未来的更新中。在此处复制(包含更多信息):stackoverflow.com/questions/40236786/…

标签: android bottomnavigationview


【解决方案1】:

从 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,则不会发生任何事情。

【讨论】:

  • 这不起作用,它只会更新选项卡本身,它不会触发 setOnNavigationItemSelectedListener 事件
  • 如果您没有在onTabSelected 中定义行为,显然它不会做任何事情。阅读文档 -> 设置选定的菜单项 ID。这与点击项目的行为相同。来自 Android 开发者
  • 我强烈建议您调试并检查您做错了什么。我已经实现了三个 BottomNavigationView 应用程序,并且从未遇到过这种方法的问题。
  • @Ricardo 当我将它添加到我的片段时,应用程序在进入该片段时挂起
  • 在我的情况下不起作用,因为我正在以编程方式创建 navigationMenu。在施工期间,点击不起作用。将所有项目添加到菜单后,调用 setSelectedItemId() 即可。
【解决方案2】:

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

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

这会正确排列所有带有标签的项目。

【讨论】:

  • 这是一个关于这个问题的 hack。您有来自库本身的方法,可以让您执行所需的操作。如果你做不到,那是因为你做错了
【解决方案3】:

对于那些仍在使用 SupportLibrary

的人

我不确定这是否是这个问题的完整答案,但我的问题非常相似 - 我必须处理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 对象不受影响?
  • @dan 根据来源它返回类属性 mMenu,而不是复制 android.googlesource.com/platform/frameworks/support/+/master/…
  • 你不需要取消选中其他项目,至少在 26.1.0/27.1.0
  • 不要取消选中,它会突出显示它们
【解决方案4】:
bottomNavigationView.setSelectedItemId(R.id.action_item1);

action_item1 是菜单项 ID。

【讨论】:

  • 这非常适合调用setOnNavigationItemSelectedListener
  • 为什么我必须一直滚动才能找到这个。没有其他答案是 api 原生的,或者太复杂了
  • 如果 bottomNavigationView 使用动态构建的菜单,仍然没有办法做到这一点。我的 MenuItems 的 id 都是 0。
  • 对我来说最好的答案。它应该是被接受的。
【解决方案5】:

使用它来按菜单 ID 设置选定的底部导航菜单项

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

【讨论】:

  • 如果使用 Jetpack Navigation,请使用它来避免导航到该项目
【解决方案6】:

使用

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

【讨论】:

  • getItem(0) 最终只会将他带到第一个标签而不保留当前标签。
  • 它不起作用。
  • 这是我在以编程方式构建菜单时可以做到的唯一方法。
【解决方案7】:

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

【讨论】:

    【解决方案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));
    }
    

    【讨论】:

      【解决方案9】:

      android:enabled="true" 添加到底部导航菜单项。

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

      【讨论】:

        【解决方案10】:

        这可能会在未来的更新中添加。但与此同时,您可以使用反射来完成此操作。

        创建一个从 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"/>
        

        【讨论】:

        • 反思是个坏主意!
        • performClick 比反射更好,恕我直言,在这种情况下。
        【解决方案11】:

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

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

        【讨论】:

          【解决方案12】:

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

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

          performIdentifierAction 采用 Menu 项目 ID 和标志。

          请参阅documentation 了解更多信息。

          【讨论】:

          • 这似乎执行了属于菜单项的操作,但不会导致菜单项显示为选中。我认为后者是 OP 想要的,但我并不肯定。
          【解决方案13】:

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

          【讨论】:

          • 我刚刚更新到 25.1.1。问题依然存在。
          【解决方案14】:

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

          【讨论】:

            【解决方案15】:

            你可以试试performClick方法:

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

            编辑

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

            【讨论】:

              【解决方案16】:
              navigationView.getMenu().findItem(R.id.navigation_id).setChecked(true);
              

              【讨论】:

                【解决方案17】:

                我向 Google 提出了一个错误,即没有可靠的方法可以在 BottomNavigationView 上选择页面:https://code.google.com/p/android/issues/detail?id=233697

                NavigationView 显然也有类似的问题,他们通过添加新的 setCheckedItem() 方法解决了这个问题。

                【讨论】:

                  【解决方案18】:

                  希望对你有帮助

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

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

                  【讨论】:

                    【解决方案19】:

                    反思是个坏主意。

                    前往gist。有一个方法可以执行选择但也调用回调:

                      @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()无论如何都不起作用。
                    【解决方案20】:
                    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,它是库的“内部”(尽管是公共的)。

                    【讨论】:

                      【解决方案21】:

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

                      这里有很多(大部分是重复或过时的)答案,但没有一个能处理非常常见的需求:动态地将不同的参数传递给加载到选项卡中的片段

                      您不能使用setSelectedItemId(R.id.second_tab) 动态地将不同的参数传递给加载的Fragment,这最终会调用静态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
                              }
                          }
                      
                      }
                      

                      【讨论】:

                        【解决方案22】:

                        这个方法对我有用。

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

                        【讨论】:

                          【解决方案23】:

                          要更改选项卡,此代码有效!

                              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
                          

                          【讨论】:

                            猜你喜欢
                            • 2017-08-31
                            • 2023-03-06
                            • 1970-01-01
                            • 2017-07-03
                            • 2016-07-09
                            • 2019-08-28
                            • 2015-11-08
                            • 1970-01-01
                            相关资源
                            最近更新 更多