如何在新的NavigationView中创建一个简单的分割线?

166

Google在Design Support Library版本22.2.0中引入了NavigationView,您可以使用菜单资源非常轻松地创建抽屉。

我如何在两个项目之间创建一个简单的分隔线? 将项目分组无效。 创建子项部分会创建分隔线,但需要标题,而我不想要标题。

任何帮助都将不胜感激。


如评论所述,已接受的答案存在问题,即可选行为在多个(并行)组之间无法工作。 为避免这种情况,请不要创建并行组,而是使用子菜单或子组来获取您的分隔符,如我在此回答中所述:https://dev59.com/t10a5IYBdhLWcg3wD08e#33877051 - Till - Appviewer.io
14个回答

338
只需要定义一个具有唯一ID的“group”。我已经检查了实现,如果组具有不同的ID,它将创建一个分隔符。
示例菜单,创建分隔符:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <group android:id="@+id/grp1" android:checkableBehavior="single" >
        <item
            android:id="@+id/navigation_item_1"
            android:checked="true"
            android:icon="@drawable/ic_home"
            android:title="@string/navigation_item_1" />
    </group>

    <group android:id="@+id/grp2" android:checkableBehavior="single" >
        <item
            android:id="@+id/navigation_item_2"
            android:icon="@drawable/ic_home"
            android:title="@string/navigation_item_2" />
    </group>
</menu>

9
不知道为什么(据我所知)这个问题没有被记录下来。我认为这将会被经常问到。谢谢! - Gaëtan
16
这种方法的一个问题是setChecked似乎在组级别上起作用。如果你选择了A组中的一个项目,然后再次打开抽屉并选择B组中的项目,两个项目都会保持高亮显示。或许Google会在下一版中修复这个行为,但目前使用NavigationView时多组的缺点就是这样。 - hungryghost
3
好的。针对你的第二组(我猜是动作而不是导航),尝试使用android:checkableBehavior="none"进行双重检查。 - espinchi
12
有趣的是,如果从“group”中删除id,它将编译并运行,但分隔符会被隐藏。 - Vahid Amiri
5
这对我也不起作用。我想要菜单组之间有分割线,而不是在每个项目后面。 - Rich Morey
显示剩余13条评论

62

创建一个可绘制的drawer_item_bg.xml,如下所示,


    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item>
        <shape android:shape="rectangle" >
            <solid android:color="#F4F4F4" />
        </shape>
    </item>

    <item android:top="-2dp" android:right="-2dp" android:left="-2dp">
        <shape>
            <solid android:color="@android:color/transparent" />
            <stroke
                android:width="1dp"
                android:color="#EAEAEA" />
        </shape>
        <!--
        android:dashGap="10px"
                android:dashWidth="10px"
                -->
    </item>

</layer-list>

将其添加到NavigationView中,作为app:itemBackground="@drawable/drawer_item_bg"

<android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:background="#F4F4F4"
        android:layout_gravity="start"
        android:fitsSystemWindows="false"
        app:menu="@menu/activity_main_drawer"
        app:itemBackground="@drawable/drawer_item_bg"/>

开始了……在此输入图像描述


1
这段代码可以工作,但它也会移除项目图标的左侧填充。至少在我的代码中是这样,在你的截图中出现的原因不清楚。 - luky
@vbp,我需要为第一个子元素添加顶部边框,为其他子元素添加底部边框。我该如何实现呢?这类似于CSS中的id.first child。 - Prabs
@Prabs 考虑使用片段、RecyclerView 或自定义视图来代替NavigationView,以获得更好的定制化。 - kttn
1
我已经使用 navMenuView.addItemDecoration(new DividerItemDecoration(NavigationViewActivity.this, DividerItemDecoration.VERTICAL)); 来实现完美的菜单项边框。 - Prabs

24

通过使用XML设置 app:itemBackground ,您可以轻松地添加分隔符。

<android.support.design.widget.NavigationView
    android:id="@id/drawer_navigation_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:fitsSystemWindows="true"
    app:menu="@menu/all_navigation_menu"
    app:itemIconTint="@color/colorPrimary"
    app:itemBackground="@drawable/bg_drawer_item"
    android:background="@color/default_background"/>

使用LayerDrawable作为背景。它保留了材料设计水波纹覆盖效果来响应点击。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/white"/>
        </shape>
    </item>
    <item android:left="@dimen/activity_horizontal_margin">
        <shape android:shape="rectangle">
            <solid android:color="@color/divider"/>
        </shape>
    </item>
    <item android:bottom="1dp">
        <shape android:shape="rectangle">
            <solid android:color="@color/white"/>
        </shape>
    </item>
</layer-list>

结果:

enter image description here


3
似乎是最好的答案,没有任何团体参与。 - Michał Ziobro
如果您想为所选项目使用自定义背景颜色,该怎么办? - ataravati
我在<shape>上遇到了一个错误:此处不允许该元素。 - Sébastien REMY

20
简单添加DividerItemDecoration:
NavigationView navigationView = (NavigationView) findViewById(R.id.navigation);
NavigationMenuView navMenuView = (NavigationMenuView) navigationView.getChildAt(0);
navMenuView.addItemDecoration(new DividerItemDecoration(MainActivity.this,DividerItemDecoration.VERTICAL));

看起来是这样的:

enter image description here


我可以将addItemDecoration行的高度更改为1像素吗? - Prabs
谢谢,你的解决方案对我有用。我很感激。兄弟,还有一个问题。是否可以删除第一项的顶部边框,并仅将边框设置为项目的底部? - viper
@viper 请参考此链接:https://www.bignerdranch.com/blog/a-view-divided-adding-dividers-to-your-recyclerview-with-itemdecoration/ - SANAT
2
每个项目的那种分隔符不遵循Material Guyidelines。 - Bo Z

11

我认为我对于多选项问题有更好的解决方案。使用我的方法,当你添加新菜单时无需担心更改代码。 我的菜单看起来与Jared编写的被接受的解决方案相同,唯一的区别在于在组上使用了 andoid:checkableBehavior="all"。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:id="@+id/first_section" android:checkableBehavior="all">
        <item
            android:id="@+id/frontpage"
            android:icon="@drawable/ic_action_home_24dp_light_blue"
            android:title="@string/drawer_frontpage" />
        <item
            android:id="@+id/search"
            android:icon="@drawable/ic_search"
            android:title="@string/drawer_search" />
    </group>
    <group android:id="@+id/second_section" android:checkableBehavior="all">
        <item
            android:id="@+id/events"
            android:icon="@drawable/ic_maps_local_movies_24dp_light_blue"
            android:title="@string/drawer_events" />
    </group>
</menu>

将'checkableBehavior'设置为'all',可以随意选择/取消单个项目,而不受其所属组的影响。您只需存储上次选中的菜单项即可。Java代码如下:

private MenuItem activeMenuItem;

@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
    // Update selected/deselected MenuItems
    if (activeMenuItem != null)
        activeMenuItem.setChecked(false);
    activeMenuItem = menuItem;
    menuItem.setChecked(true);

    drawerLayout.closeDrawers();
    return true;
}

10

我不确定这个问题是在API 26 (Android 8)中已经修复还是一直存在。对于 Android 编程,我仍然是一个新手。但是我按照以下方式添加了父组。

  1. 我有分隔符。
  2. nav_g1nav_g2 中选择任何项都会取消选择其他项。
  3. 由于嵌套组没有添加额外的填充。
  4. 我没有向监听器添加任何Java代码。

菜单代码

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <group
            android:id="@+id/nav_g1"
            android:checkableBehavior="single">
            <item
                android:id="@+id/nav_1"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_1"/>
            <item
                android:id="@+id/nav_2"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_2"/>
        </group>
        <group
            android:id="@+id/nav_g2"
            android:checkableBehavior="single">
            <item
                android:id="@+id/nav_3"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_3"/>
            <item
                android:id="@+id/nav_4"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_4"/>
        </group>
    </group>
    <item android:title="Actions">
        <menu>
            <item
                android:id="@+id/nav_7"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_7"/>
            <item
                android:id="@+id/nav_8"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_8"/>
        </menu>
    </item>
</menu>

笔记

  1. 正如答案中提到的,每个组需要具有唯一的ID以显示分隔符。
  2. 根据答案,空格与谷歌材料设计规范相匹配。
  3. 对于父组,必须设置android:checkableBehavior="single",这样就不会出现答案中的多个选定菜单项问题(由 hungryghost 在评论中提到)。

以下是截图:

设备屏幕截图


8

像Nilesh的答案一样创建不同的组确实会在它们之间创建一个分隔符,但是会导致导航抽屉中有两个选中的项目,这是一个问题。

我处理这个问题的简单方法是:

    public boolean onNavigationItemSelected(final MenuItem menuItem) {

    //if an item from extras group is clicked,refresh NAV_ITEMS_MAIN to remove previously checked item
    if (menuItem.getGroupId() == NAV_ITEMS_EXTRA) {


        navigationView.getMenu().setGroupCheckable(NAV_ITEMS_MAIN, false, true);
        navigationView.getMenu().setGroupCheckable(NAV_ITEMS_EXTRA, true, true);
       }else{

        navigationView.getMenu().setGroupCheckable(NAV_ITEMS_MAIN, true, true);
        navigationView.getMenu().setGroupCheckable(NAV_ITEMS_EXTRA, false, true);


    }
    //Update highlighted item in the navigation menu
    menuItem.setChecked(true);
}

2
@espinchi - 如果其他组包含导航菜单项,则这种方法将无法工作。此解决方案仅适用于操作。不是良好的用户体验实践。 - Mushtaq Jameel
IS onNavigationItemSelected(final MenuItem menuItem) { 这是默认方法吗?我应该在哪里编写这段代码? - John
NAV_ITEMS_MAIN 是什么? - John
@John - 是的,这是NavigationView.OnNavigationItemSelectedListener默认实现的方法。 NAV_ITEMS_MAIN只是menuItem组的R.id.,例如:在menu.xml中它是R.id.groupIdMain。 - Zorawar Sachdev
@ZorawarSachdev 非常感谢您,缺省方法非常清晰,我已经实现了它。另一个疑问是,<group id == NAV_ITEMS_EXTRA and items id = NAV_ITEMS_MAIN 是否正确?我在同一组中有四个项目,我该如何添加行? - John
显示剩余3条评论

5
我想在第一个项目前和最后一个项目后添加分隔符。使用@Nilesh的解决方案,我不得不添加虚拟菜单项并将其启用为false,这感觉非常低劣。另外,如果我想在我的菜单中做其他很酷的事情怎么办?
我注意到NavigationView扩展了FrameLayout,因此您可以像对待FrameLayout一样放置自己的内容。然后我设置了一个空的菜单xml,以便它只显示我的自定义布局。我知道这可能违反谷歌希望我们采取的路径,但如果您想要一个真正定制的菜单,这是一种简单的方法。
<!--Note: NavigationView extends FrameLayout so we can put whatever we want in it.-->
<!--I don't set headerLayout since we can now put that in our custom content view-->
<android.support.design.widget.NavigationView
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:fitsSystemWindows="true"
    app:menu="@menu/empty_menu">

    <!--CUSTOM CONTENT-->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- CUSTOM HEADER -->
        <include
            android:id="@+id/vNavigationViewHeader"
            layout="@layout/navigation_view_header"/>

        <!--CUSTOM MENU-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_below="@+id/vNavigationViewHeader">

            <View style="@style/NavigationViewLineSeperator"/>

            <Button android:text="Option 1"/>

            <View style="@style/NavigationViewLineSeperator"/>

            <Button android:text="Option 2"/>

            <View style="@style/NavigationViewLineSeperator"/>

        </LinearLayout>
    </RelativeLayout>
</android.support.design.widget.NavigationView>

这里是样式。
<style name="NavigationViewLineSeperator">
        <item name="android:background">@drawable/line_seperator</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">1dp</item>
</style>

和drawable相关

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="@color/white"/>
    <size android:width="100sp"
          android:height="1sp" />
</shape>

而且这个菜单
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
</menu>

编辑:

您可以使用带有绘制的TextView来代替按钮,这样可以模仿原始菜单项的外观:

 <TextView
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:text="@string/menu_support"
     android:drawableLeft="@drawable/menu_icon_support"
     style="@style/MainMenuText" />

// style.xml
<style name="MainMenuText">
    <item name="android:drawablePadding">12dp</item>
    <item name="android:padding">10dp</item>
    <item name="android:gravity">center_vertical</item>
    <item name="android:textColor">#fff</item>
</style>

1
谢谢,显然最方便的方法是将自定义视图作为菜单项。 - Jan Rabe
我也使用这个解决方案,因为它让我更容易地设置项目的自定义字体并更改分隔符颜色。 - luky

1

调用需要API级别28:

只需使用

 menu?.setGroupDividerEnabled(true)

在 onCreateOptionsMenu 函数中。
示例:
override fun onCreateOptionsMenu(menu: Menu?): Boolean {

        menuInflater.inflate(R.menu.main_menu, menu)

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
            menu?.setGroupDividerEnabled(true)
        }
        
        return super.onCreateOptionsMenu(menu)


    }

1

此解决方案归功于Zorawar,抱歉重复答案,但无法在评论中添加太多格式化文本。

Zorawar的解决方案有效,代码可以改进如下:

public void onNavigationItemSelected(int groupId) {

    //if an item from extras group is clicked,refresh NAV_ITEMS_MAIN to remove previously checked item
    navigationView.getMenu().setGroupCheckable(NAV_ITEMS_MAIN, (groupId == NAV_ITEMS_MAIN), true);
    navigationView.getMenu().setGroupCheckable(NAV_ITEMS_EXTRA, (groupId == NAV_ITEMS_EXTRA), true);


    //Update highlighted item in the navigation menu
    menuItem.setChecked(true);
}

什么是NAV_ITEMS_MAIN、NAV_ITEMS_EXTRA和groupid? - John
它们是group_id的常量。 - Mushtaq Jameel

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