我正在尝试使用设计库中的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因为:
是否有其他方法,最好是更简单的方法来实现这一目标,或者这是我们对当前版本库的最佳选择?
(作为旁注,我想知道这个解决方案中的proguard规则是否必要以及它做了什么?我不知道关于proguard的任何事情,但是这个项目是继承自启用它的其他人。)
答案 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)
如果我已正确理解你的问题(我可能没有这样做),那么更好的解决办法可能是解决这个问题。这些是我对你的问题的假设:
如果我的假设是正确的,那么有两个更好的解决方案:
那就是说,如果你确实想按照自己的方式去做,我认为下面的代码可以通过改变受影响的项目来实现它。 (你应尽可能避免反射,这通常表明你的设计存在另一个架构问题)
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
}
}