取消选中BottomNavigationView中没有Dummy Item或Reflection

时间:2017-03-14 20:11:59

标签: android android-proguard bottomnavigationview

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

首先我这样做了:

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. 我之前已经读过,如果可能的话应该避免反思。
  4. 是否有其他方法,最好是更简单的方法来实现这一目标,或者这是我们对当前版本库的最佳选择?

    (作为旁注,我想知道这个解决方案中的proguard规则是否必要以及它做了什么?我不知道关于proguard的任何事情,但是这个项目是继承自启用它的其他人。)

5 个答案:

答案 0 :(得分:4)

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

(menu.getItem(i) as? MenuItemImpl)?.let {
    it.isExclusiveCheckable = false
    it.isChecked = it.itemId == actionId
    it.isExclusiveCheckable = true
}

答案 1 :(得分:2)

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

答案 2 :(得分:1)

如果我已正确理解你的问题(我可能没有这样做),那么更好的解决办法可能是解决这个问题。这些是我对你的问题的假设:

  • 您有一系列活动
  • 每个Activity都有自己的BottomNavigationView
  • 当您在一个活动上单击BNV时,单击的项目将被选中
  • 您想要取消选择所点击的项目,因为当新的活动开始时,未选择任何内容

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

  1. 使用碎片而不是活动(推荐
    • 他们BNV停留在一个活动上,活动中的片段改变
  2. 请勿取消选择已点击的项目
    • 启动时的每个活动都会选择匹配的正确磁贴
  3. 那就是说,如果你确实想按照自己的方式去做,我认为下面的代码可以通过改变受影响的项目来实现它。 (你应尽可能避免反射,这通常表明你的设计存在另一个架构问题)

    bnv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
         @Override
         public boolean onNavigationItemSelected(@NonNull MenuItem item) {
             item.getActionView().setSelected(false);
             return false;
         }
    });
    

答案 3 :(得分:1)

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

需要proguard规则的原因是因为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");
    }

}

答案 4 :(得分: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
                }
            }