BottomNavigationView中的菜单项未居中

4
我有一个底部导航视图,没有图标,只有文本字段。
我想要将文本垂直和水平居中,并为不同的状态添加高亮显示:
topnavigationview.setItemBackgroundResource(R.drawable.mainactivitybackgroundhighlight_top);

使用XML代码:

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

视图是这样包含的:

<android.support.design.widget.BottomNavigationView
            android:id="@+id/top_navigation_view"
            android:layout_width="match_parent"
            android:layout_height="35dp"
            android:layout_below="@id/include4"
            android:background="@color/dunkelgraublau"
            app:itemTextColor="@color/weiß"
            app:menu="@menu/top_navigation_menu" />

然而,现在它看起来像这样:

enter image description here

如您所见,高亮部分并没有覆盖菜单的一半,更像是40%; 选择右侧项目时也是如此 - 尽管宽度设置为match_parent。
文本也不垂直居中也不水平居中;
1)如何在布局中垂直居中菜单文本?
2)如何使两个菜单项占用导航视图的50%? 这将水平居中文本并使高亮部分占用50%。
2个回答

6
真正的答案是:不建议这样做。您正在尝试使用一个旨在具有特定外观和感觉的支持设计库组件,但是您想要使它看起来和感觉不同。您应该创建自己的视图以满足您的需求。
但是...
1)如何将菜单文本垂直居中显示?
深入研究BottomNavigationView的实现,我们最终发现菜单项由BottomNavigationItemView对象(派生自FrameLayout)表示,它们使用这个布局:https://github.com/dandar3/android-support-design/blob/master/res/layout/design_bottom_navigation_item.xml 因此,我们可以创建一个方法来调整标签视图的父级重力和填充,以便它们出现在居中位置:
private static void adjustGravity(View v) {
    if (v.getId() == android.support.design.R.id.smallLabel) {
        ViewGroup parent = (ViewGroup) v.getParent();
        parent.setPadding(0, 0, 0, 0);

        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) parent.getLayoutParams();
        params.gravity = Gravity.CENTER;
        parent.setLayoutParams(params);
    }

    if (v instanceof ViewGroup) {
        ViewGroup vg = (ViewGroup) v;

        for (int i = 0; i < vg.getChildCount(); i++) {
            adjustGravity(vg.getChildAt(i));
        }
    }
}

2) 如何使得两个菜单项都占据导航视图的50%?

每个菜单项的宽度受到BottomNavigationMenuView类中此代码的限制:

mActiveItemMaxWidth = res.getDimensionPixelSize(
        R.dimen.design_bottom_navigation_active_item_max_width);

没有好的方法来修改这个值,但我们可以使用反射来做到:

private static void adjustWidth(BottomNavigationView nav) {
    try {
        Field menuViewField = nav.getClass().getDeclaredField("mMenuView");
        menuViewField.setAccessible(true);
        Object menuView = menuViewField.get(nav);

        Field itemWidth = menuView.getClass().getDeclaredField("mActiveItemMaxWidth");
        itemWidth.setAccessible(true);
        itemWidth.setInt(menuView, Integer.MAX_VALUE);
    }
    catch (NoSuchFieldException e) {
        // TODO
    }
    catch (IllegalAccessException e) {
        // TODO
    }
}

一旦您在活动中拥有这些方法,您只需编写以下内容:
BottomNavigationView nav = findViewById(R.id.top_navigation_view);
adjustGravity(nav);
adjustWidth(nav);

请注意,由于这些方法都依赖于BottomNavigationView的内部实现细节,因此当您更新支持/设计库时,它们总是容易出现故障。

感谢您深入的回复,非常好用!我知道这种支持库修改不是最佳实践,稍后会研究更优雅的解决方案。您的第二个方法包含两个// TODO注释;您将在那里编写什么代码呢? - Gérard
1
@TeraGerard 由你决定。如果抛出了那些异常,视图宽度将不会改变。如果你不在意,那么你可以什么也不做(或向日志系统发送一个非致命性的错误消息,这样你就可以看到某些东西已经出错)。如果你在意,那么你需要添加一些代码,尝试其他解决方案,或者简单地throw new RuntimeException(e)来崩溃你的应用程序。 - Ben P.

2

这个答案对我有帮助。这是Ben P.的kotlin版本:

    private fun adjustGravity(v: View) {
        if (v.id == android.support.design.R.id.smallLabel) {
            val parent = v.parent as ViewGroup
            parent.setPadding(0, 0, 0, 0)

            val params = parent.layoutParams as FrameLayout.LayoutParams
            params.gravity = Gravity.CENTER
            parent.layoutParams = params
        }

        if (v is ViewGroup) {
            val vg = v as ViewGroup

            for (i in 0 until vg.childCount) {
                adjustGravity(vg.getChildAt(i))
            }
        }
    }

    private fun adjustWidth(nav: BottomNavigationView) {
        try {
            val menuViewField = nav.javaClass.getDeclaredField("mMenuView")
            menuViewField.isAccessible = true
            val menuView = menuViewField.get(nav)

            val itemWidth = menuView.javaClass.getDeclaredField("mActiveItemMaxWidth")
            itemWidth.isAccessible = true
            itemWidth.setInt(menuView, Integer.MAX_VALUE)
        } catch (e: NoSuchFieldException) {
            // TODO
        } catch (e: IllegalAccessException) {
            // TODO
        }
    }

然后...
    val bottomNavigationView: BottomNavigationView = findViewById(R.id.bottom_navigation)

    adjustGravity(bottomNavigationView)
    adjustWidth(bottomNavigationView)

enter image description here


4
在AndroidX支持库中,您需要将 android.support.design.R.id.smallLabel 更改为 com.google.android.material.R.id.smallLabel - jordiz

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