Android Material Design: Nav Drawer Width

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26562531/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 11:50:32  来源:igfitidea点击:

Material Design: Nav Drawer Width

androidandroid-appcompatmaterial-design

提问by zubietaroberto

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

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

side nav width = screen width - app bar height

side nav width = screen width - app bar height

How do we implement this on android?

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:

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.

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.

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?

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

采纳答案by Logan

With the Android Design Support Libraryit is now really simple to implement navigation drawer including correct sizing. Use the NavigationViewand 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.

With the Android Design Support Libraryit is now really simple to implement navigation drawer including correct sizing. Use the NavigationViewand 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:

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.

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.

回答by Matthew Kairys

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.

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_materialdimension reference for the margin.

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_materialdimension reference for the margin.

Follow these steps:

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 NavDrawerstyle 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"
    />
    
  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 NavDrawerstyle 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"
    />
    

回答by jmart

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

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

Here:

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.

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:

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.

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

回答by vovkab

They already updated specs, now navigation drawer width is:

They already updated specs, now navigation drawer width is:

Math.min(screenWidth — actionBarSize, 6 * actionBarSize);

回答by JupiterT

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.

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.

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.

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); 


            }
        }

    }
    ); 

回答by Sotti

The Navigation Drawer width depends on the device smallest width, orientation and Android version.

The Navigation Drawer width depends on the device smallest width, orientation and Android version.

Check out this article about sizing Navigation Drawer.

Check out this article about sizing Navigation Drawer.