Implementation of Navigation Drawer according to Material Design

早些时候,这篇文章讨论了Material Design风格的抽屉式导航到底应该如何设计(Google的官方应用也有不同的展示),下面我们就来实现文章里面提到的效果。

Earlier days, This Article discussed how to design Navigation Drawer according to Material Design style, and now we’ll implement this style mentioned there;

首先看一下效果(未完全展开):

Preview (partly opened):

 

preview_navigationdrawer

 

抽屉显示在ActionBar之上,在SystemStatusBar之下。

As shown, the navigation drawer is above the ActionBar and below the System Status Bar;

实现方法

Implementation

实现这个效果的关键是ToolBar的使用,ToolBar是Support v7包中的控件,用以让ActionBar更灵活的兼容老版本的Android设备。

The key of this effects is ToolBar, from Support v7 library, witch can take ActionBar into old version devices more flexible.

下面是MainActivity的布局文件:

There is the layout xml of MainActivity:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                        xmlns:tools="http://schemas.android.com/tools"
                                        android:id="@+id/drawer_layout"
                                        android:layout_width="match_parent"
                                        android:layout_height="match_parent"
                                        tools:context="com.kyleduo.theday.ui.activity.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v7.widget.Toolbar
            android:id="@+id/main_toolbar"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:minHeight="?attr/actionBarSize"
            android:fitsSystemWindows="true"
            android:elevation="4dp"
            android:background="@color/primary_color"/>

        <FrameLayout
            android:id="@+id/main_content_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!-- content goes here -->
        </FrameLayout>

    </LinearLayout>

    <fragment
        android:id="@+id/main_drawer_fragment"
        android:layout_width="@dimen/drawer_width"
        android:layout_height="match_parent"
        android:name="com.kyleduo.demo.DrawerFragment"
        tools:layout="@layout/fragment_drawer"
        android:fitsSystemWindows="false"
        android:elevation="10dp"
        android:layout_gravity="start"
        />

</android.support.v4.widget.DrawerLayout>
和一般的DrawerLayout用法基本是一致的,只是在内容FrameLayout外面又包裹了一层Layout,用以引入ToolBar。因为ToolBar属于内容布局的子布局,所以自然位于抽屉的下方。 It is almost the same with how-to-use of DrawerLayout before, besides the outer Layout of FrameLayout used for ToolBar. Because ToolBar is inside the content layout, so it is under the Drawer naturally. 使用ToolBar需要将默认的ActionBar隐藏掉。style.xml文件如下: To use ToolBar, it is necessary to hide the default ActionBar. style.xml looks like this.
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
    </style>

    <style name="CustomTheme" parent="Theme.AppCompat">
        <item name="android:actionBarStyle">@style/CustomActionBar</item>
    </style>

    <style name="CustomTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="android:windowActionBar">false</item>
    </style>

    <style name="CustomTheme.NoActionBar.Translucent">

    </style>

    <style name="CustomActionBar" parent="Widget.AppCompat.ActionBar">
        <item name="android:background">@color/primary_color</item>
    </style>

</resources>
本例中使用android:windowActionBar属性隐藏ActionBar,也可以让应用主题直接继承自xxx.NoActionBar主题实现这个效果。CustomTheme.NoActionBar.Translucent主题是为了针对其他API优化而扩展出的主题,也是Activity设置的主题,这样书写方便利用Android的资源后缀进行适配。 I use android:windowActionBar to hide ActionBar here, but you can also make your theme extends from theme xxx.NoActionBar to get that. Theme CustomTheme.NoActionBar.Translucent is used to be easier to write other compatible theme with multiple style.xml using suffix of resources. Android 4.4版本开始引入半透明状态栏效果,但是4.4版本里面的半透明实际上是一个渐变,直接显示该效果会显得ActionBar出奇的宽,所以Demo中使用SystemBarTint库叠加了一层阴影,这样看起来效果要好一些。MainActivity的onCreate()方法如下: Translucent status bar effect is added since Android 4.4, but it is actually a ramp in android 4.4 and would make ActionBar looks wider than it should be. So I use library SystemBarTint to overlay a shadow there and it looks better. Here is the onCreate() method of MainActivity:
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
        setSupportActionBar(toolbar);

        getSupportFragmentManager().beginTransaction().add(R.id.main_content_container, new ContentFragment()).commit();

        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
            SystemBarTintManager tintManager = new SystemBarTintManager(this);
            tintManager.setStatusBarTintEnabled(true);
            tintManager.setStatusBarTintResource(R.color.status_bar_color);
        }
    }
其他代码,请访问Github:[Demo](https://github.com/kyleduo/MDNavigationDrawer) To get the whole project source code, please visit Github: [Demo](https://github.com/kyleduo/MDNavigationDrawer). ### **其他实现方法** ### **Other implementation ways** 如果不使用ToolBar,只使用默认的ActionBar,可以实现吗?答案也是肯定的。 Is't possible to deal with this without ActionBar? The answer is YES. 默认的ActionBar在Activity的contentView之外,所以不能通过xml解决。从SlidingMenu项目中借鉴思路,即:在onCreate方法中,通过DecorView,获取到ActionBar的根View,然后将其放到抽屉布局之下,但是如此实现会使得内容布局上方出现不必要的空白,代码不够优雅,推荐采用博文上方的方法。第二种方法参考如下内容:[StackOverFlow](http://stackoverflow.com/questions/23294954/android-navigation-drawer-on-top-actionbar) Default ActionBar is outside the contentView so xml can not solve this problem. By stealing from SlidingMenu library, I got this solution: in onCreate method, fetch the root view of ActionBar using DecorView and put it under drawer layout. But this will generate an unnecessary space at the top of content and make the code not that elegant. So just do it like my blog. The second way refer this: [StackOverFlow](http://stackoverflow.com/questions/23294954/android-navigation-drawer-on-top-actionbar) 核心代码如下:
    // Inflate the "decor.xml"
    LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    DrawerLayout drawer = (DrawerLayout) inflater.inflate(R.layout.decor, null); // "null" is important.

    // HACK: "steal" the first child of decor view
    ViewGroup decor = (ViewGroup) getWindow().getDecorView();
    View child = decor.getChildAt(0);
    decor.removeView(child);
    FrameLayout container = (FrameLayout) drawer.findViewById(R.id.container); // This is the container we defined just now.
    container.addView(child);

    // Make the drawer replace the first child
    decor.addView(drawer);