通过 Navigation View 创建导航抽屉

随着 Google I/O 2015,新的 Android Design Support Library 也出现了。

Android Design Support Library 给开发者带来了一些重要的 Material Design 组件,并且向下兼容到 Android 2.1,Navigation View就是其中之一,可用于方便地创建导航抽屉。

效果如下:

因为需要向下兼容,所以以下内容的 Activity 均继承于AppCompatActivity,使用的主题的父主题均为 AppCompat 的主题。

基本步骤

通过Navigation View创建导航抽屉首先自然需要引入这个支持包:

compile 'com.android.support:design:22.2.0'

布局文件中加入以下代码:

<android.support.v4.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <!-- 需要呈现的内容 -->

    <android.support.design.widget.NavigationView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/drawer_header"
            app:menu="@menu/drawer"/>
</android.support.v4.widget.DrawerLayout>

其中app:headerLayout用于指定一个任意的布局,作为导航抽屉的顶部,如效果图中的紫色带 Username 字样部分。

app:menu用于指定导航抽屉的菜单项,具体代码如下:

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_home"
            android:icon="@drawable/ic_dashboard"
            android:title="Home" />
        <item
            android:id="@+id/nav_messages"
            android:icon="@drawable/ic_event"
            android:title="Messages" />
        <item
            android:id="@+id/nav_friends"
            android:icon="@drawable/ic_headset"
            android:title="Friends" />
        <item
            android:id="@+id/nav_discussion"
            android:icon="@drawable/ic_forum"
            android:title="Discussion" />
    </group>

    <item android:title="Sub items">
        <menu>
            <item
                android:icon="@drawable/ic_dashboard"
                android:title="Sub item 1" />
            <item
                android:icon="@drawable/ic_forum"
                android:title="Sub item 2" />
        </menu>
    </item>

</menu>

其中通过<group android:checkableBehavior="single">设定一组菜单项为至多只有一个可被选中,非常适合用于通过导航抽屉切换呈现的 Fragment,若需要默认选中一个菜单项则只需要给指定 item 加上android:checked="true"即可。并且可以通过子菜单的形式显示分割线和子标题。

到此为止布局文件部分就完成了,接下来只需要在Activity中设置监听器即可:

mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
if (navigationView != null) {
    navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(MenuItem menuItem) {
            //切换相应 Fragment 等操作
            menuItem.setChecked(true);
            mDrawerLayout.closeDrawers();
            return false;
        }
    });
}

至此导航抽屉就基本完成了,但 Material Design 中建议导航抽屉应该在 status bar 上也显示,而 Android 5.0 及以上版本支持这一特性,因此还需要进一步处理。

针对 Android 5.0 及以上版本

首先由 window 来绘制 status bar 的背景,否则 status bar 为系统默认背景(原生是黑色背景),而 AppCompat 的主题已带如下属性:

<item name="android:windowDrawsSystemBarBackgrounds">true</item>

确保 Android 5.0 以上版本使用的主题中并没有把该属性设置成 false 即可。

而通过 window 来绘制 status bar 的背景并不一定是我们需要的最终结果,因为 status bar 的背景在需要呈现的内容上和导航抽屉上不一定是一样的,而通过 window 来绘制则肯定是一样的,因此把 window 绘制的 status bar 的背景设置为透明,在 Android 5.0 以上版本使用的主题中加入如下代码:

<item name="android:statusBarColor">@android:color/transparent</item>

接下来只需要DrawerLayout来接管 status bar 的背景的绘制工作即可,给布局文件中的DrawerLayout加入如下属性:

android:fitsSystemWindows="true"

此时导航抽屉已经能够显示在 status bar 上。

至此理论上 Android 2.1 以上版本都能正常显示导航抽屉了,完整代码可参照 Github 上的一个 Demo

通过Navigation View能够实现上图所示的导航抽屉,但有两处若不设置依旧会占着控件,十分不合理,分别是图标以及 4 处的子菜单标题。所以若需要没有图标的项目或单独加分割线则还得考虑使用自定义布局的形式实现导航抽屉。

Navigation View优点依旧很明显,首先设置的图标经测试会被改变成合适的颜色,无论是选中还是未选中状态,无需任何代码即可达成下图效果,若使用自定义布局的形式可能需要设计未选中和选中两套图标或自己写代码动态改变颜色。

若还是觉得Navigation View不够好,又觉得自定义太麻烦,并且只需要兼容到 Android 2.3.3 或以上版本的话可以考虑使用使用 MaterialDrawer 开源控件,十分强大,能够实现Navigation View所有能够实现的效果,并且自由度更高,我通过这个开源控件完成了我毕业设计中所需的两个导航抽屉,具体效果如下图所示:

参考资料

Android Design Support Library(Android Developers Blog)

  • Ten Wong2015-08-12 18:17

    对分割线我也找了好久。 原来在group 里加上不同的id就可以实现。
    http://stackoverflow.com/questions/30625280/how-to-create-a-simple-divider-in-the-new-navigationview

  • Ten Wong2015-08-12 18:18

    评论的网站 为什么不能加二级域名,如https://github.com/awong1900

  • ihsan.lee2015-08-12 18:48

    之前写这篇是因为没看到说得比较详细的,现在看到用这个 Android Design Support Library 会增大不少 apk 的大小,加上其中的其他控件还有不少 bug,所以其实现在看来自己实现或许是更好的选择所以就没有再更新这篇文章了。

    关于不能填二级域名的原因我也不知道,我是因为偷懒不自己架博客,直接用了 Farbox 的服务,所以评论系统用的也是自带的,现在看起来略简陋,我不知道怎么回复某个评论,也可能是我没好好了解而已。

    目前在准备出国手续,在此期间比较有时间,正在尝试学习 iOS 开发,虽然 Android 也还没学得怎样,但就想趁着还有精力多尝试一下,免得一工作时间变少后就很难尝试新东西了,因此目前也没什么时间维护博客,等到 iOS 学到目前 Android 的程度后可能才会开始折腾一下博客,看看是在 Farbox 上加入第三方评论系统还是自己重新搭建了。