材料设计:导航抽屉宽度

Posted

技术标签:

【中文标题】材料设计:导航抽屉宽度【英文标题】:Material Design: Nav Drawer Width 【发布时间】:2014-12-21 03:41:56 【问题描述】:

根据the Material Design specs移动设备上导航抽屉的宽度必须是

侧边导航宽度 = 屏幕宽度 - 应用栏高度

我们如何在 android 上实现这个?

我有两个部分解决方案。首先是骇人听闻的方式:在包含活动中,我输入了以下代码:

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) 最终显示 display = getWindowManager().getDefaultDisplay(); 最终点大小 = new Point(); display.getSize(大小); 最终 ViewGroup.LayoutParams 参数 = mDrawerFragment.getView().getLayoutParams(); params.width = size.x - getResources().getDimensionPixelSize( R.dimen.abc_action_bar_default_height_material ); mFragmentUserList.getView().setLayoutParams(params);

但是,这会导致第二个布局循环并且在姜饼中不起作用:它不是最佳的。

第二种解决方案涉及在片段和抽屉布局之间添加一个空间。但是,它会取代阴影和用户可以按下以返回主应用程序的位置。当按下“汉堡包”图标时,它也会崩溃。也不是最优的。

是否有更好的解决方案,最好是涉及样式和 xml 的解决方案?

【问题讨论】:

【参考方案1】:

我设法使用 XML 样式声明制作了一个解决方案,但它也有点笨拙。我的方法是使用边距而不是应用设置的宽度,以避免编写任何代码来手动计算布局。我创建了一个基本的样式规则来强调如何让它发挥作用。

不幸的是,DrawerLayout 目前应用的最小边距为 64dp。为了使这种方法起作用,我们需要用负边距来抵消该值,以便我们可以获得导航抽屉所需的宽度。希望将来可以解决此问题 (Someone has filed an issue regarding it),这样我们就可以参考 abc_action_bar_default_height_material 尺寸参考作为边距。

按照以下步骤操作:

    添加以下维度和样式定义:

    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>
    

    在项目中添加上述规则后,您可以在导航抽屉视图中引用 NavDrawer 样式:

    layout/navigation_drawer.xml(或用于导航抽屉的其他适当视图)

    <?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_
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#FFF"
    />
    

【讨论】:

应该被接受为已回答,这个信息太棒了。谢谢大佬。【参考方案2】:

在寻找更简单的解决方案后,我发现了一篇非常澄清的文章:Material Navigation Drawer sizing。

这里:

Nexus 5 屏幕是一个不错的 640 x 360 dp (xxhdpi),应用栏 它高 56 dp。所以导航抽屉应该是:

[以 dp 为单位的宽度] = 360dp — 56dp = 304dp

Nexus 4 的屏幕为 640 x 384 dp (xhdpi) 反而。相同的 56dp 应用栏高度。它的导航抽屉?

[宽度 in dp] = 384dp — 56dp = 328dp

那么,Google 设计师是如何设计的 分别是 288dp 和 304dp 宽度?我不知道。

Google 应用,开启 另一方面,似乎同意我的数学。哦,你知道是什么 最有趣的是这一切? iPhone(具有不同的屏幕 高度,但恒定的 320 dp 宽度)被正确标记为具有 264dp 导航抽屉。

基本上,它表明有关导航抽屉的一些准则自相矛盾,您可以使用以下规则来避免计算:

你基本上总是可以在 -sw360dp 上使用 304dp 和 320dp on -sw384dp 为你的导航抽屉,你会做对的。

【讨论】:

【参考方案3】:

他们已经更新了规格,现在导航抽屉的宽度是:

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

【讨论】:

"侧边导航的宽度等于屏幕的宽度减去操作栏的高度,或者在这种情况下距屏幕右边缘 56dp。导航的最大宽度抽屉是标准增量的 5 倍(移动设备上为 56dp,平板电脑上为 64dp)。”你从哪里得到 6 * actionbarSize ? 是的,它是 5,但 5 看起来不正确,而 6 看起来是文档中的错误类型。我正在寻找一种提交此文件的方法,但找不到任何方法。 顺便说一句,如果你检查他们的截图并测量它,你会发现它绝对不是 5 :) google.com/design/spec/patterns/navigation-drawer.html【参考方案4】:

嗯,我发现这很难理解和实施。不幸的是,马修的解决方案使我的导航抽屉对于横向来说太宽了,这似乎与谷歌的做法相反,即导航宽度由设备的最小宽度决定。无论如何,它在我的情况下不起作用,因为我在清单中禁用了配置。所以我决定用下面的代码动态改变导航宽度。

需要注意的是,我的应用仅适用于手机,我选择 320dp 作为最大宽度。这也是我将两个方向的工具栏高度定为 56dp 的原因。

希望它对某人有所帮助,并且他们可以避免它给我带来的不必要的压力。

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


            
        

    
    ); 

【讨论】:

【参考方案5】:

Navigation Drawer 宽度取决于设备最小宽度、方向和 Android 版本。

Check out this article about sizing Navigation Drawer.

【讨论】:

【参考方案6】:

有了Android Design Support Library,现在实现抽屉导航非常简单,包括正确的大小。使用NavigationView 并使用其从菜单资源中制作抽屉的功能(例如here),或者您可以将它包裹在您当前用于显示抽屉列表的视图(例如ListView、RecyclerView)周围。然后 NavigationView 将为您处理抽屉大小。

这是我如何使用包裹在 ListView 周围的 NavigationView 的示例:

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

    <!-- Layout where content is shown -->
    <RelativeLayout
        android:layout_
        android:layout_>

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

        <FrameLayout
            android:id="@+id/content_frame"
            android:layout_
            android:layout_
            android:layout_below="@id/toolbar" />

        <!-- Toolbar shadow for pre-lollipop -->
        <View style="@style/ToolbarDropshadow"
            android:layout_
            android:layout_
            android:layout_below="@id/toolbar" />

    </RelativeLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_
        android:layout_
        android:layout_gravity="start">

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

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

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

通过这种方式,您可以使用 NavigationViews 调整大小,并且仍然使用您自己的抽屉列表。虽然从菜单资源中制作抽屉列表要容易得多(例如here),但您不能对列表项使用自定义视图。

【讨论】:

这对我来说效果很好。我认为这应该是正确的答案,因为它不需要开发人员的编码工作。然而,能够完全填充 Google 的导航抽屉宽度指南。 @zubietaroberto 是的,我当然知道。但仍有可能有人遇到此类问题,并且可能会觉得这很有用。 NavigationView 具有android:fitsSystemWindows="true" 时,此方法会导致子布局呈现在状态/导航栏下(并且没有必要的填充)。有什么好办法解决吗?

以上是关于材料设计:导航抽屉宽度的主要内容,如果未能解决你的问题,请参考以下文章

如何更改材料设计导航抽屉中汉堡图标的颜色

工具栏和导航抽屉

创建一个看起来像材料设计指南的 SearchView

当按下后退按钮并打开导航抽屉时,我的应用程序关闭而不是关闭抽屉

Android Kitkat 4.4版本状态栏下使用导航抽屉时状态栏颜色显示为白色不透明

导航抽屉在 Android Studio 中不起作用