41

我想创建一个全宽导航抽屉。设置layout_widthmatch_parenton会@+id/left_drawer产生大约 80% 的屏幕空间宽度。这似乎是标准行为。我必须覆盖onMeasure()DrawerLayout

我当前的代码:

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

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/black"
        android:id="@+id/mainFragmentContainer">
    </FrameLayout>

    <include
        android:id="@+id/left_drawer"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        layout="@layout/drawer"/>
</android.support.v4.widget.DrawerLayout>

谢谢。

4

15 回答 15

130

如果您想要更简单的解决方案,您可以设置负边距

android:layout_marginLeft="-64dp"

对于您的 left_drawer:

<include
        android:id="@+id/left_drawer"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        layout="@layout/drawer"
        android:layout_marginLeft="-64dp"/>
于 2013-10-30T09:18:45.650 回答
30

是的,您必须扩展 DrawerLayout 并覆盖一些方法,因为MIN_DRAWER_MARGINprivate

这是一个可能的解决方案:

public class FullDrawerLayout extends DrawerLayout {

    private static final int MIN_DRAWER_MARGIN = 0; // dp

    public FullDrawerLayout(Context context) {
        super(context);
    }

    public FullDrawerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FullDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalArgumentException(
                    "DrawerLayout must be measured with MeasureSpec.EXACTLY.");
        }

        setMeasuredDimension(widthSize, heightSize);

        // Gravity value for each drawer we've seen. Only one of each permitted.
        int foundDrawers = 0;
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);

            if (child.getVisibility() == GONE) {
                continue;
            }

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            if (isContentView(child)) {
                // Content views get measured at exactly the layout's size.
                final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
                        widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
                final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
                        heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
                child.measure(contentWidthSpec, contentHeightSpec);
            } else if (isDrawerView(child)) {
                final int childGravity =
                        getDrawerViewGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;
                if ((foundDrawers & childGravity) != 0) {
                    throw new IllegalStateException("Child drawer has absolute gravity " +
                            gravityToString(childGravity) + " but this already has a " +
                            "drawer view along that edge");
                }
                final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
                        MIN_DRAWER_MARGIN + lp.leftMargin + lp.rightMargin,
                        lp.width);
                final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
                        lp.topMargin + lp.bottomMargin,
                        lp.height);
                child.measure(drawerWidthSpec, drawerHeightSpec);
            } else {
                throw new IllegalStateException("Child " + child + " at index " + i +
                        " does not have a valid layout_gravity - must be Gravity.LEFT, " +
                        "Gravity.RIGHT or Gravity.NO_GRAVITY");
            }
        }
    }

    boolean isContentView(View child) {
        return ((LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY;
    }

    boolean isDrawerView(View child) {
        final int gravity = ((LayoutParams) child.getLayoutParams()).gravity;
        final int absGravity = Gravity.getAbsoluteGravity(gravity,
                child.getLayoutDirection());
        return (absGravity & (Gravity.LEFT | Gravity.RIGHT)) != 0;
    }

    int getDrawerViewGravity(View drawerView) {
        final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
        return Gravity.getAbsoluteGravity(gravity, drawerView.getLayoutDirection());
    }

    static String gravityToString(int gravity) {
        if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
            return "LEFT";
        }
        if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
            return "RIGHT";
        }
        return Integer.toHexString(gravity);
    }

}
于 2013-08-05T20:22:38.557 回答
30

因为所有这些答案都不适用于 OS 6.0.1,所以我将在此处发布对我有用的解决方案DrawerLayout+ NavigationView

所以我所做的就是以NavigationView编程方式改变宽度:

mNavigationView = (NavigationView) findViewById(R.id.nv_navigation);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) mNavigationView.getLayoutParams();
params.width = metrics.widthPixels;
mNavigationView.setLayoutParams(params);

这适用于所有屏幕尺寸。

于 2015-12-22T12:29:28.357 回答
16

根据Robert's Answer,您可以使用layout_marginLeft=-64dp轻松解决此问题。

但是,它似乎不再适用于 Android 5.0 及更高版本。所以这是对我有用的解决方案。

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginRight="-64dp"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginRight="64dp"/>

    <include
        android:id="@+id/left_drawer"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        layout="@layout/drawer"/>

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

基本上,添加android:layout_marginRight="-64dp"到根目录DrawerLayout,这样所有布局都将转到 64dp 的右侧。

然后我将其添加layout_marginRight=64dp到内容中,使其回到原始位置。然后你可以在那里有一个完整的抽屉。

于 2016-02-08T02:48:34.093 回答
9

Grogory 解决方案的变体:

在获取对抽屉布局的引用后,我立即调用以下实用程序方法,而不是子类化:

/**
 * The specs tell that
 * <ol>
 * <li>Navigation Drawer should be at most 5*56dp wide on phones and 5*64dp wide on tablets.</li>
 * <li>Navigation Drawer should have right margin of 56dp on phones and 64dp on tablets.</li>
 * </ol>
 * yet the minimum margin is hardcoded to be 64dp instead of 56dp. This fixes it.
 */
public static void fixMinDrawerMargin(DrawerLayout drawerLayout) {
  try {
    Field f = DrawerLayout.class.getDeclaredField("mMinDrawerMargin");
    f.setAccessible(true);
    f.set(drawerLayout, 0);

    drawerLayout.requestLayout();
  } catch (Exception e) {
    e.printStackTrace();
  }
}
于 2014-12-17T05:06:21.633 回答
4

另一种在不覆盖太多的情况下解决问题的可能方法:

public class FullScreenDrawerLayout extends DrawerLayout {

... //List of constructors calling
... //super(...);
... //init();

/** Make DrawerLayout to take the whole screen. */
protected void init() {
    try {

        Field field = getClass().getSuperclass().getDeclaredField("mMinDrawerMargin");
        field.setAccessible(true);
        field.set(this, Integer.valueOf(0));

    } catch (Exception e) {
        throw new IllegalStateException("android.support.v4.widget.DrawerLayout has changed and you have to fix this class.", e);
    }
}

}

如果在某个时候更新了支持库并且 mminDrawerMargin 不再存在,您将在发布下一次更新之前遇到异常并修复问题。

我没有进行测量,但假设没有那么多反射会影响性能。此外,它仅在每个视图创建时执行。

PS这很奇怪为什么DrawerLayout在这一点上变得如此不灵活(我是关于私人最小保证金)......

于 2013-12-27T10:34:44.107 回答
4

Nipper 的 FullDrawerLayout 类简直太棒了.. 它的性能也比默认抽屉更快,但是你不能;不能在没有 view.getLayoutDirection() 的 api 设备上使用它;(即:类不适用于所有姜饼设备)

所以我所做的是

全部替换

view.getLayoutDirection();

使用以下代码

GravityCompat.getAbsoluteGravity(gravity,ViewCompat.getLayoutDirection(this));

我已将我的支持库更新到最新版本,并将 fullDrawerlayout 扩展到支持导航抽屉。现在它也可以正常工作 Gingerbread 设备

于 2013-10-22T14:42:40.077 回答
4

试试这对我有用:

<include
    android:id="@+id/left_drawer"
    android:orientation="vertical"
    android:layout_width="320dp"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    layout="@layout/drawer"/>

设置包含布局的宽度android:layout_width="320dp"。对于具有不同屏幕尺寸的设备,您可以动态设置此包含布局的宽度。

于 2013-11-28T13:51:19.327 回答
3

你可以使用这个。受这篇文章的启发,我已经升级到第 5 版。因为它在版本 5 及更高版本中存在 StatusBar 问题。

您必须扩展 DrawerLayout 并覆盖一些方法,因为 MIN_DRAWER_MARGIN 是私有的

public class FullDrawerLayout extends DrawerLayout {

    private static final int MIN_DRAWER_MARGIN = 0; // dp

    public FullDrawerLayout(Context context) {
        super(context);
    }

    public FullDrawerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FullDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalArgumentException(
                    "DrawerLayout must be measured with MeasureSpec.EXACTLY.");
        }

        setMeasuredDimension(widthSize, heightSize);

        //for support Android 5+
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
            FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams();
            params.topMargin = getStatusBarHeight();
            setLayoutParams(params);
        }

        // Gravity value for each drawer we've seen. Only one of each permitted.
        int foundDrawers = 0;
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);

            if (child.getVisibility() == GONE) {
                continue;
            }

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            if (isContentView(child)) {
                // Content views get measured at exactly the layout's size.
                final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
                        widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
                final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
                        heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
                child.measure(contentWidthSpec, contentHeightSpec);
            } else if (isDrawerView(child)) {
                final int childGravity =
                        getDrawerViewGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;
                if ((foundDrawers & childGravity) != 0) {
                    throw new IllegalStateException("Child drawer has absolute gravity " +
                            gravityToString(childGravity) + " but this already has a " +
                            "drawer view along that edge");
                }
                final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
                        MIN_DRAWER_MARGIN + lp.leftMargin + lp.rightMargin,
                        lp.width);
                final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
                        lp.topMargin + lp.bottomMargin,
                        lp.height);
                child.measure(drawerWidthSpec, drawerHeightSpec);
            } else {
                throw new IllegalStateException("Child " + child + " at index " + i +
                        " does not have a valid layout_gravity - must be Gravity.LEFT, " +
                        "Gravity.RIGHT or Gravity.NO_GRAVITY");
            }
        }
    }

    boolean isContentView(View child) {
        return ((LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY;
    }

    boolean isDrawerView(View child) {
        final int gravity = ((LayoutParams) child.getLayoutParams()).gravity;
        final int absGravity = Gravity.getAbsoluteGravity(gravity,
                child.getLayoutDirection());
        return (absGravity & (Gravity.LEFT | Gravity.RIGHT)) != 0;
    }

    int getDrawerViewGravity(View drawerView) {
        final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
        return Gravity.getAbsoluteGravity(gravity, drawerView.getLayoutDirection());
    }

    static String gravityToString(int gravity) {
        if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
            return "LEFT";
        }
        if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
            return "RIGHT";
        }
        return Integer.toHexString(gravity);
    }


    public int getStatusBarHeight() {
        int result = 0;
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

}
于 2017-08-12T09:05:35.663 回答
1

你可以通过下面的代码

 int width = getResources().getDisplayMetrics().widthPixels/2;
        DrawerLayout.LayoutParams params = (android.support.v4.widget.DrawerLayout.LayoutParams) drawer_Linear_layout.getLayoutParams();
        params.width = width;
        drawer_Linear_layout.setLayoutParams(params);
于 2014-12-24T10:21:40.680 回答
1
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include
            layout="@layout/app_bar_dashboard"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>


    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_marginRight="32dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true">

        <include layout="@layout/view_navigation_menu" />

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

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

这对我来说非常有效。希望帮助别人。

于 2017-11-03T05:45:49.270 回答
0

根据此处的 UI 指南,Google 建议最大宽度为 320 dip 。此外,可以通过指定left_drawer ListView的layout_width来设置宽度。

于 2014-01-22T08:18:44.370 回答
0
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".UserListActivity">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignParentTop="true"
    android:background="@drawable/common_gradient"
    android:layoutDirection="rtl"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.2">

        <TextView
            android:id="@+id/userType_textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:text="نوع المستخدم"
            android:textColor="#000000"
            android:textSize="20sp"
            tools:text="نوع المستخدم" />

        <TextView
            android:id="@+id/className_textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/userType_textView"
            android:layout_centerHorizontal="true"
            android:text="إسم القسم"
            android:textColor="#000000"
            android:textSize="16sp"
            tools:text="إسم القسم" />

        <ImageButton
            android:layout_width="30dp"
            android:layout_height="20dp"
            android:layout_alignBottom="@+id/userType_textView"
            android:layout_marginLeft="15dp"
            android:layout_marginStart="15dp"
            android:background="@android:color/transparent"
            android:contentDescription="@string/desc"
            android:onClick="showMenuAction"
            android:scaleType="fitCenter"
            android:src="@drawable/menu" />
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.8"

        android:background="#FAFAFA">

        <SearchView
            android:id="@+id/user_searchView"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:background="#9CC3D7" />

        <ListView
            android:id="@+id/users_listView"
            android:layout_width="100dp"
            android:layout_height="100dp"

            android:layout_alignParentBottom="true"
            android:layout_below="@+id/user_searchView"
            android:layout_centerHorizontal="true"
            android:divider="#DFDEE1"
            android:dividerHeight="1dp" />
    </RelativeLayout>

</LinearLayout>

<android.support.v4.widget.DrawerLayout
    android:id="@+id/navigationDrawerUser"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    android:layoutDirection="rtl">


    <ExpandableListView
        android:id="@+id/menu_listView_user"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#195269"
        android:choiceMode="singleChoice"
        android:divider="#2C637D"
        android:dividerHeight="1dp"
        android:groupIndicator="@null">

    </ExpandableListView>

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

于 2017-05-22T14:09:59.110 回答
0

大家都觉得全宽 Sidebar Drawer 布局创建很复杂,但是按照这个布局模式就很简单了,不需要设置任何负值。

这是我的MainActivity.xml

<androidx.drawerlayout.widget.DrawerLayout
    android:id="@+id/drawerLayout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/white"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Main Activity -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <include
            android:id="@+id/toolbarMain"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            layout="@layout/layout_profile_toolbar"/>

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navGraph="@navigation/app_navigation" />

    </LinearLayout>
    <!-- Main Activity End -->

    <!-- Custom Navigation Drawer Start -->
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true">

        <include
            android:id="@+id/custom_nav"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            layout="@layout/fragment_profile"/>

    </com.google.android.material.navigation.NavigationView>
    <!-- Custom Navigation Drawer End -->

</androidx.drawerlayout.widget.DrawerLayout>
于 2021-06-03T14:22:06.907 回答
-1

您还可以查看SlidingDrawer类。这是一个已弃用的类,但正如文档所述,您可以根据其源代码编写自己的实现。

于 2013-06-13T23:45:16.680 回答