底部导航视图 - 如何取消所有菜单项的选中状态并保持标题显示?

28

我喜欢BottomNavigationView的设计,所以我决定使用它来实现我的应用程序的新菜单,而不仅仅是使用简单的按钮。

我将this帖子作为指南。

根据BottomNavigationView文档,它的目的是

在应用程序的顶级视图之间提供快速导航。 它主要设计用于移动设备。

在我的情况下,我只想让每个MenuItem启动一个活动,但默认情况下总是有一个MenuItem被选中:

enter image description here

我尝试使用以下代码将颜色设置为白色:

app:itemIconTint="@color/white"
app:itemTextColor="@color/white"

但是,明显选择的MenuItem与其他菜单项不同(标题更大),这仍然让我感到困扰:

enter image description here

我将为您翻译以下编程相关内容:

我提出了一个想法,即将隐藏的MenuItem放置在选择中,如下所示:

<item
android:id="@+id/uncheckedItem"
android:title="" />

并使其视图 GONE
 bottomNavigationView.getMenu().findItem(R.id.uncheckedItem).setChecked(true);
 bottomNavigationView.findViewById(R.id.uncheckedItem).setVisibility(View.GONE);

这会使所有的菜单项都取消选中,但默认情况下,BottomNavigationView 隐藏标题,因为它有超过3个菜单项要显示,即使第四个 MenuItem 被设置为 GONE

enter image description here

所以我的问题仍然存在,有没有一种方法/黑客可以取消选择所有的菜单项,并保持其标题显示?

你做得没问题,但需要向你的 BottomNavigationView 添加一个属性。请看我的回答。 - Dan Bray
请尝试通过在XML菜单中将“uncheckedItem”的可见性设置为false来验证我的答案,而不是通过编程实现。 - Amrutha Saj
11个回答

43
mNavigationBottom.getMenu().setGroupCheckable(0, false, true);

13
这会取消选择所有项目,没有任何副作用。但是,你必须稍后在某个地方添加“mNavigationBottom.getMenu().setGroupCheckable(0, true, true);”以允许再次显示选定的项目。 - MacD
这会移除所选菜单项的颜色,但是标题仍然以某种原因为粗体显示。 - nhcodes

31

为取消选择所有项目,我创建了这个扩展:

fun BottomNavigationView.uncheckAllItems() {
    menu.setGroupCheckable(0, true, false)
    for (i in 0 until menu.size()) {
        menu.getItem(i).isChecked = false
    }
    menu.setGroupCheckable(0, true, true)
}

通过调用menu.setGroupCheckable(0, true, false)方法,可以实现设置菜单项为可勾选状态并且不具有互斥性,在循环内改变勾选状态之后,再次将菜单设为互斥状态即可完成操作。

这里是文档链接


1
这就是我想要的。所有菜单都取消选中状态。 - Naveen Kumar M
这是方法。 - cliveleehere
这使得标题对我消失了一次。 - Boldijar Paul
太好了,这对我来说没问题(也适用于Material 3)。你可以简化取消选中所有项目的操作,像这样:menu.forEach { it.isChecked = false }(使用预定义的扩展函数)。 - Ridcully

5

感谢您的想法。我已经在我的库中实现了它。 我有一个更好的方法通过反射来做,所以它不会显示空格。

如果您有兴趣,请点击这里:https://github.com/ittianyu/BottomNavigationViewEx

private void initBottomViewAndLoadFragments(final BottomNavigationViewEx bnve) {
    bnve.enableAnimation(false);
    bnve.enableShiftingMode(false);
    bnve.enableItemShiftingMode(false);

    // use the unchecked color for first item
    bnve.setIconTintList(0, getResources().getColorStateList(R.color.bnv_unchecked_black));
    bnve.setTextTintList(0, getResources().getColorStateList(R.color.bnv_unchecked_black));

    bnve.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {

        private boolean firstClick = true;
        private int lastItemId = -1;

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            // restore the color when click
            if (firstClick) {
                firstClick = false;
                bnve.setIconTintList(0, getResources().getColorStateList(R.color.selector_bnv));
                bnve.setTextTintList(0, getResources().getColorStateList(R.color.selector_bnv));
            }

            if (firstClick || lastItemId == -1 || lastItemId != item.getItemId()) {
                lastItemId = item.getItemId();
            } else {
                return false;
            }

            // do stuff
            return fillContent(item.getItemId());
        }
    });
}

-- res/color/selector_bnv.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/bnv_checked_white" android:state_checked="true" />
    <item android:color="@color/bnv_unchecked_black" />
</selector>

-- res/values/colors.xml

<color name="bnv_checked_white">@android:color/white</color>
<color name="bnv_unchecked_black">@android:color/black</color>

1
干得好!请写一下如何使第一次未选中。在您的存储库中没有找到此代码。 - oxied
更新:在他的代码仓库中找到:https://github.com/ittianyu/BottomNavigationViewEx/blob/master/app/src/main/java/com/ittianyu/bottomnavigationviewexsample/features/style/StyleActivity.java#L152 - oxied
这应该是正确的答案。很棒的库,感谢提供示例。 - Neonigma

4

您的解决方案似乎改变了项目之间的空格。

这是我的解决方案:

“只需将所选颜色设置为未选颜色相同即可。”

例如:

private void changeMenuItemCheckedStateColor(BottomNavigationView bottomNavigationView, String checkedColorHex, String uncheckedColorHex) {
    int checkedColor = Color.parseColor(checkedColorHex);
    int uncheckedColor = Color.parseColor(uncheckedColorHex);

    int[][] states = new int[][] {
            new int[] {-android.R.attr.state_checked}, // unchecked
            new int[] {android.R.attr.state_checked}, // checked

    };

    int[] colors = new int[] {
            uncheckedColor,
            checkedColor
    };

    ColorStateList colorStateList = new ColorStateList(states, colors);

    bottomNavigationView.setItemTextColor(colorStateList);
    bottomNavigationView.setItemIconTintList(colorStateList);

}

如果您想取消选中所有项目,可以:
changeMenuItemCheckedStateColor(mBottomNavigationView, "#999999", "#999999");

如果您想恢复颜色设置,您可以

changeMenuItemCheckedStateColor(mBottomNavigationView, "FF743A", "999999");

3
我发现了一个解决方案,将我的进展与 这个 帖子合并。

步骤:

  1. 更新proguard-rules.pro并同步构建
  2. 创建帮助程序以禁用BottomNavigationView Shift模式
  3. 在Menu.xml中创建要隐藏的项目
  4. 膨胀BottomNavigationView
  5. 将要隐藏的项目设置为Checked GONE
  6. 使用Helper来禁用Shifting Mode

输出:

enter image description here

工作代码:

proguard-rules.pro:

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

BottomNavigationShiftHelper.java:

public class BottomNavigationShiftHelper {

    private final static String TAG = "DEBUG_BOTTOM_NAV_UTIL";

    public static void disableShiftMode(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);
                // set once again checked value, so view will be updated
                item.setChecked(item.getItemData().isChecked());
            }
        } catch (NoSuchFieldException e) {
            Log.d(TAG, "Unable to get shift mode field");
        } catch (IllegalAccessException e) {
            Log.d(TAG, "Unable to change value of shift mode");
        }
    }
}

Activity Sample.java:

 private void loadNavigationBar() {
        BottomNavigationView bottomNavigationView = (BottomNavigationView)
                findViewById(R.id.navigation_bar);

        bottomNavigationView.getMenu().findItem(R.id.uncheckedItem).setChecked(true);
        bottomNavigationView.findViewById(R.id.uncheckedItem).setVisibility(View.GONE);

        BottomNavigationViewUtils.disableShiftMode(bottomNavigationView);

        bottomNavigationView.setOnNavigationItemSelectedListener(
                new BottomNavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                        switch (item.getItemId()) {
                            case R.id.newList:
                                //Do The Math
                                break;
                            case R.id.loadList:
                                //Do The Math
                                break;
                            case R.id.settings:
                                //Do The Math
                                break;
                        }
                        return false;
                    }
                });
    }

BottomNavigationMenu.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/newList"
        android:enabled="true"
        android:icon="@drawable/new_list"
        android:title="@string/common.button.list.new"
        app:showAsAction="withText" />
    <item
        android:id="@+id/loadList"
        android:enabled="true"
        android:icon="@drawable/load"
        android:title="@string/common.button.list.load"
        app:showAsAction="withText" />
    <item
        android:id="@+id/settings"
        android:enabled="true"
        android:icon="@drawable/settings"
        android:title="@string/common.label.settings"
        app:showAsAction="withText" />
    <item
        android:id="@+id/uncheckedItem"
        android:title="" />
</menu>

BottomNavigationComponent(在Activity.xml中):

<android.support.design.widget.BottomNavigationView
    android:id="@+id/navigation_bar"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    app:itemIconTint="@color/white"
    app:itemTextColor="@color/white"
    android:background="@drawable/BottomNavigationMenu.xml"
    app:menu="@menu/supercart_bottom_navigation" />

它能用,但是我的底部导航栏没有占满整个宽度...你能帮我吗? - Reprator
我遇到了以下问题,https://github.com/ittianyu/BottomNavigationViewEx/issues/25 - Reprator
3
我有5个项目,如果我添加一个空项目,就会出现以下错误:BottomNavigationView支持的项目最多为5个。 - Kusan
@Kusan,请查看我下面的答案。希望对你有用 https://dev59.com/jlgR5IYBdhLWcg3wZcm1#46491753 - tim.paetz
我在调整这个菜单上浪费了太多时间,后来找到了更好的方法(虽然不是最理想的),现在我为每个用户类别使用不同的菜单资源。 - Marc

2

试一下这个,对我有效

 <item
    android:id="@+id/uncheckedItem"
    android:visible="false"
    android:title="" />

并设置

bottomNavigationView.getMenu().findItem(R.id.uncheckedItem).setChecked(true);

enter image description here

您会发现所有菜单项都未被选中;因为选择是针对不可见的uncheckedItem进行的。

希望这能帮到您。


1
仅适用于在底部导航菜单中使用不超过5个项目的情况,因此您需要将其中1个菜单项指定为不可见(android:visible="false")。 - BENN1TH

1

在被接受的答案中有一个例外,我们设置了最大项,但无法实现该代码。所以我找到了一段比被接受的代码更简单的代码,它也可以处理最大项。

我参考了这里的内容:Custom TextSize of BottomNavigationView support android

你可以在你的dimen.xml文件中添加:

<dimen name="design_bottom_navigation_text_size" tools:override="true">10sp</dimen>
<dimen name="design_bottom_navigation_active_text_size" tools:override="true">10sp</dimen>

通过这样做,您将覆盖BottomNavigationView内部类使用的dimen的默认值。因此,请小心操作。
在初始化底部导航视图的onCreate方法中设置此代码。
mNavigationBottom.getMenu().setGroupCheckable(0, false, true);

最后,将底部导航栏设置如下:

 <com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/nav_view"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:layout_gravity="bottom"
    android:background="?android:attr/windowBackground"
    android:fitsSystemWindows="true"
    app:labelVisibilityMode="labeled"
    app:layout_anchorGravity="fill"
    app:menu="@menu/bottom_nav_menu" />

在此将您的app:labelVisibilityMode设置为labeled

0

最佳答案mNavigationBottom.getMenu().setGroupCheckable(0, false, true)对我没有用。它仍然显示第一项被选中。

对我有用的是添加一个不可见的项目并选择它,就像你在问题中所做的那样。

app:labelVisibilityMode="labeled"添加到您的BottomNavigationView中,以便所有项目都保持在视图中并显示它们的标题。


0

这与被接受的答案相同,只是我改变了下面标记的两行代码。 当遍历BottomNavigationItemViews时,我总是将checked设置为false,并且还将checkable设置为false。 这可以防止菜单项更改大小。 您仍然需要这个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); // <--- This line changed
                item.setCheckable(false);  // <-- This line was added
            }
        }
        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");
        }

   }

0
    if(isDarkMode(context)) {
        mBotNavView.setItemIconTintList(ColorStateList.valueOf(Color.parseColor("#FFFFFF")));
    } else {
        mBotNavView.setItemIconTintList(ColorStateList.valueOf(Color.parseColor("#000000")));
    }

    public boolean isDarkMode(Context context) {
        int nightModeFlags = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
        return darkMode = (nightModeFlags == Configuration.UI_MODE_NIGHT_YES);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接