Android – Material Design: Nav Drawer Width

androidandroid-appcompatmaterial-design

According to the Material Design specs the Nav Drawer's width on mobile devices must be

side nav width = screen width – app bar height

How do we implement this on android?

I have two partial solutions. First is the hacky way: in the containing activity I put this code:

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) {
    final Display display = getWindowManager().getDefaultDisplay();
    final Point size = new Point();
    display.getSize(size);

    final ViewGroup.LayoutParams params = mDrawerFragment.getView().getLayoutParams();
    params.width = size.x - getResources().getDimensionPixelSize(
        R.dimen.abc_action_bar_default_height_material
    );
    mFragmentUserList.getView().setLayoutParams(params);
}

This, however, causes a second layout cycle and doesn't work in gingerbread: it is not optimal.

The second solution involves adding a Space between the fragment and the drawerLayout. It however, displaces the shadow and the spot where the user can press to return to the main app. It also crashes when the "hamburguer" icon is pressed. Not optimal either.

Is there a better solution, preferably one that involves styles and xml?

Best Answer

I managed to make a solution using XML style declarations but it is a bit hacky as well. My approach was to use margins instead of applying a set width to avoid writing any code to calculate the layout manually. I've created a basic style rule to highlight how to get this working.

Unfortunately, DrawerLayout currently applies a minimum margin of 64dp. For this approach to work, we need to offset that value with negative margins so we can get the desired width for the navigation drawer. Hopefully this can be resolved in the future (Someone has filed an issue regarding it) so we can just reference the abc_action_bar_default_height_material dimension reference for the margin.

Follow these steps:

  1. Add the following dimension and style definitions:

    values/dimens.xml

    <!-- Match 56dp default ActionBar height on portrait orientation -->
    <dimen name="nav_drawer_margin_offset">-8dp</dimen>
    

    values-land/dimens.xml

    <!-- Match 48dp default ActionBar height on landscape orientation -->
    <dimen name="nav_drawer_margin_offset">-16dp</dimen>
    

    values/styles.xml

    <!-- Nav drawer style to set width specified by Material Design specification -->
    <style name="NavDrawer">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_marginRight">@dimen/nav_drawer_margin_offset</item>
    </style>
    

    values-sw600dp/styles.xml

    <!-- Margin already matches ActionBar height on tablets, just modify width -->
    <style name="NavDrawer">
        <item name="android:layout_width">320dp</item>
        <item name="android:layout_marginRight">0dp</item>
    </style>
    
  2. Once you have added the rules above in your project, you can reference the NavDrawer style in your navigation drawer view:

    layout/navigation_drawer.xml (or other appropriate view being used for your navigation drawer)

    <?xml version="1.0" encoding="utf-8"?>
    <ListView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/navigation_drawer"
        style="@style/NavDrawer"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#FFF"
    />