6Answers
  • 11
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

Material Design: Nav Drawer Width

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?

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"
    />
    
  • 32
Reply Report

With the Android Design Support Library it is now really simple to implement navigation drawer including correct sizing. Use the NavigationView and either use its ability to make drawer out of menu resource (example here) or you can just wrap it around the view which you currenty use for showing your drawer list (e.g. ListView, RecyclerView). NavigationView will then take care of the drawer sizing for you.

Here's an example how I use the NavigationView wrapped around ListView:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/navdrawer_layout"
    android:fitsSystemWindows="true">

    <!-- Layout where content is shown -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include android:id="@+id/toolbar"
            layout="@layout/toolbar" />

        <FrameLayout
            android:id="@+id/content_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/toolbar" />

        <!-- Toolbar shadow for pre-lollipop -->
        <View style="@style/ToolbarDropshadow"
            android:layout_width="match_parent"
            android:layout_height="3dp"
            android:layout_below="@id/toolbar" />

    </RelativeLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start">

        <ListView
            android:id="@+id/navdrawer_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:choiceMode="singleChoice"
            android:dividerHeight="0dp"
            android:divider="@null"/>

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

</android.support.v4.widget.DrawerLayout>

This way you can use NavigationViews sizing and still use your own drawer list. Though it is much easier to make the drawer list out of menu resource (example here) you can't use custom views for the list items.

  • 28
Reply Report
      • 1
    • When the NavigationView has android:fitsSystemWindows="true", this approach causes the child layout to be rendered under the status/navigation bar (and not have the necessary padding). Is there a good way to fix that?
      • 1
    • This works pretty well for me. I think this should be correct answer, as it requires no coding effort from developer. Yet, able to full fill Google's navigation drawer width guidelines.
    • @zubietaroberto Yes, of course, I know that. But there still might be someone having trouble with this kind of problem and may find this useful.

After looking for a simpler solution, I found a very clarifying article: Material Navigation Drawer sizing.

Here:

The Nexus 5 screen is a nice 640 x 360 dp (xxhdpi), and an app bar on it is 56 dp tall. So the nav drawer should be:

[width in dp] = 360dp?—?56dp = 304dp

A Nexus 4 sports a 640 x 384 dp (xhdpi) screen instead. Same 56dp app bar height. Its nav drawer?

[width in dp] = 384dp?—?56dp = 328dp

So, how did the Google designers come up with 288dp and 304dp widths, respectively? I have no idea.

Google apps, on the other hand, seem to agree with my maths. Oh, and you know what the funniest thing is in all this? The iPhone (which has different screen heights, but a constant 320 dp width) is marked correctly as having a 264dp nav drawer.

Basically, it shows that some guidelines about the navigation drawer contradict themselves and that you can use the following rule to avoid calculations:

You can basically always use 304dp on -sw360dp and 320dp on -sw384dp for your navigation drawer, and you'll get it right.

  • 8
Reply Report

They already updated specs, now navigation drawer width is:

Math.min(screenWidth — actionBarSize, 6 * actionBarSize);
  • 4
Reply Report

Well I found this very difficult to understand and implement. Unfortunately, Matthew's solution made my nav drawer too wide for landscape, and it seemed contrary to Google's practices, i.e. the nav width is determined by the device's smallest width. In any event it wouldn't work in my case as I disabled the configuration in my manifest. So I decided to changed the nav width dynamically with the following code.

It should be noted my app is just for phones and I settled on 320dp being the maximum width. This is also why I've settled on 56dp toolbar height for both orientations.

Hope it helps someone, and that they can avoid the unnecessary stress it caused me.

navDrawLayout.post(new Runnable()
    {

        @Override
        public void run() {

            Resources r = getResources();

            DisplayMetrics metrics = new DisplayMetrics();

            if(r.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){

                getWindowManager().getDefaultDisplay().getMetrics(metrics);
                int height = metrics.heightPixels;

                float screenWidth = height / r.getDisplayMetrics().density;
                float navWidth = (screenWidth - 56); 

                navWidth = Math.min(navWidth, 320); 

                int newWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, navWidth, r.getDisplayMetrics());

                DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) navDrawLayout.getLayoutParams(); 
                params.width = newWidth; 
                navDrawLayout.setLayoutParams(params);      


            }

            if(r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {

            getWindowManager().getDefaultDisplay().getMetrics(metrics);
            int width = metrics.widthPixels;

            float screenWidth = width / r.getDisplayMetrics().density;
            float navWidth = screenWidth - 56; 

            navWidth = Math.min(navWidth, 320); 

            int newWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, navWidth, r.getDisplayMetrics());

            DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) navDrawLayout.getLayoutParams(); 
            params.width = newWidth; 
            navDrawLayout.setLayoutParams(params); 


            }
        }

    }
    ); 
  • 0
Reply Report