5

我正在尝试使用设计库中的 BottomNavigationView。一切正常,除了我希望每个导航项启动一个活动,因此我想取消选中导航中的所有项目,使它们看起来相同。我尝试了几种解决方案,其中大多数都不起作用,最后一个确实有效,但感觉很hacky。

首先我这样做:

ViewGroup nav = (ViewGroup) bottomNav;
for(int i=0; i < nav.getChildCount(); i++) {
    nav.getChildAt(i).setSelected(false);
}

这似乎什么也没做。

然后我尝试了:

int size = bottomNav.getMenu().size();
for (int i = 0; i < size; i++) {
    bottomNav.getMenu().getItem(i).setChecked(false);
}

这只检查了最后一项而不是第一项。

最后我尝试在菜单中添加一个虚拟项目并执行以下操作:

bottomNav.getMenu().findItem(R.id.dummmy_item).setChecked(true);
bottomNav.findViewById(R.id.dummmy_item).setVisibility(View.GONE);

这几乎可以工作,但它隐藏了下面的标题,这对我的情况很重要。

然后我找到了这个答案:https ://stackoverflow.com/a/41372325/4888701并编辑了我上面的解决方案以包含它。具体来说,我添加了 proguard 规则,并使用了该确切的帮助程序类并调用了该方法。它看起来正确,似乎有效。但这对我来说感觉hacky,因为:

  1. 我正在使用一个虚拟菜单项来允许检查任何可见项目
  2. 它为应该是一个小的视觉修复添加了相当多的代码。
  3. 我之前读过,如果可能的话,应该避免反思。

有没有其他的,最好是更简单的方法来实现这一点,或者这是我们当前版本的库中最好的?

(作为旁注,我想知道这个解决方案中的 proguard 规则是否必要以及它的作用?我对 proguard 真的一无所知,但这个项目是从启用它的其他人那里继承的。)

4

5 回答 5

6

经过大量的反复试验,这对我有用(使用 Kotlin)

(menu.getItem(i) as? MenuItemImpl)?.let {
    it.isExclusiveCheckable = false
    it.isChecked = it.itemId == actionId
    it.isExclusiveCheckable = true
}
于 2017-04-10T13:59:01.230 回答
3

@Joe Van der Vee 解决方案对我有用。我已经从中制作了扩展方法。但我认为这是否没有像@RestirctedApi 抑制这样的缺点!

@SuppressLint("RestrictedApi")
fun BottomNavigationView.deselectAllItems() {
    val menu = this.menu

    for(i in 0 until menu.size()) {
        (menu.getItem(i) as? MenuItemImpl)?.let {
            it.isExclusiveCheckable = false
            it.isChecked = false
            it.isExclusiveCheckable = true
        }
    }
}
于 2018-12-17T14:33:19.427 回答
1

我知道要求不使用反射的问题,但是,我还没有找到另一种方法来获得所需的效果而不使用它。有一个代码修复允许禁用 git repo 中的 shift 模式,但谁知道何时发布。所以暂时(26.0.1),这段代码对我有用。人们说不使用反射的原因也是因为它在 Android 上很慢(尤其是在旧设备上)。但是,对于这个调用,它不会对性能产生影响。但是,在解析/序列化大量数据时应该避免它。

需要 proguard 规则的原因是因为 proguard 混淆了您的代码。这意味着它可以更改方法名称、截断名称、分解类以及它认为合适的任何其他内容,以防止有人能够阅读您的源代码。此规则防止此字段变量名称发生更改,因此当您通过反射调用它时,它仍然存在。

保护规则:

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

更新方法:

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); // <-- Changed this line
            item.setCheckable(false); // <-- Added this line
        }
    }
    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");
    }

}
于 2017-09-29T15:14:34.573 回答
1

如果我正确理解了您的问题(我可能没有正确理解),那么更好的解决方案可能是解决这个问题。这些是我对您的问题的假设:

  • 你有一组活动
  • 每个 Activity 都有自己的 BottomNavigationView
  • 当您在一项活动上单击 BNV 时,单击的项目将变为选中状态
  • 您想取消选择单击的项目,因为当新 Activity 启动时没有选择任何内容

如果我的假设是正确的,那么有两个更好的解决方案:

  1. 使用片段而不是活动(推荐
    • 他们 BNV 停留在一个活动上,活动中的片段发生变化
  2. 不要取消选择单击的项目
    • 每个活动开始时都会选择要匹配的正确图块

也就是说,如果您确实想按照自己的方式进行操作,我认为下面的代码将通过在受影响的项目更改时更改它来实现它。(你应该尽可能避免反射,它通常表明你的设计存在另一个架构问题)

bnv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
     @Override
     public boolean onNavigationItemSelected(@NonNull MenuItem item) {
         item.getActionView().setSelected(false);
         return false;
     }
});
于 2017-03-17T14:10:44.437 回答
0

我刚遇到这个问题。我在这里找到了工作解决方案,将 menuItem 转换为 MenuItemImp,并使用 @RestrictTo(LIBRARY_GROUP_PREFIX) 进行了注释。如果你不想使用这个受限类,你可以坚持使用标准的 MenuItem,而不是使用 isExclusiveCheckable,只需使用 isCheckable。

例子:

navigation_view?.menu?.let {
                for(menuItem in it.iterator()){
                    menuItem.isCheckable = false
                    menuItem.isChecked = false
                    menuItem.isCheckable = true
                }
            }
于 2020-02-21T13:44:34.900 回答