Full width Navigation Drawer

痞子三分冷 提交于 2020-01-09 08:56:08

问题


I'd like to create a full width navigation drawer. Setting layout_width to match_parent on @+id/left_drawer yields in width of about 80% of screen space. This seems to be the standard behavior. Do I have to override onMeasure() of DrawerLayout?

My current code:

<?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>

Thanks.


回答1:


Yes, you have to extend DrawerLayout and override some methods because MIN_DRAWER_MARGIN is private

Here is a possible solution:

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

}



回答2:


If you want simpler solution you can just set negative margin

android:layout_marginLeft="-64dp"

for your 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"/>



回答3:


Because all these answers did not work on OS 6.0.1, I'll post here the solution that worked for me in combination with DrawerLayout + NavigationView.

So all what I do is change the width of the NavigationView programatically:

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

This works for all screen sizes.




回答4:


Based on the Robert's Answer, you can use the layout_marginLeft=-64dp to solve this problem easily.

However it doesn't seems to work anymore on Android 5.0 and above. So here's my solution that worked for me.

<?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>

Basically, Add android:layout_marginRight="-64dp" to the root DrawerLayout so all the layout will go to the right for 64dp.

Then I add the layout_marginRight=64dp to the content so it goes back to the original position. Then you can have a full drawer there.




回答5:


A variant on Grogory's solution:

Instead of subclassing I call the following utility method right after I grab a reference to the drawer layout:

/**
 * 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();
  }
}



回答6:


Nipper's FullDrawerLayout Class is just simply awesome.. it's performance is also faster than the default drawer how ever you can;t use it on devices with api that don't have view.getLayoutDirection(); (i'e : Class doesn;t work on all gingerbread devices )

so what i did was

replaced all

view.getLayoutDirection();

with the below code

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

I have my support library updated to the latest also have extended the fullDrawerlayout to the support navigational drawer. Now it works fine Gingerbread devices as well




回答7:


Another possible way to solve the issue without overriding too much:

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

}

If, at some point, support library is updated and mMinDrawerMargin is not there anymore you will get exception and fix problem before you publish your next update.

I didn't make measurements, but suppose there is not so many reflection to affect performance. Furthermore, it executes only per view creation.

PS it's strange why DrawerLayout is made so inflexible (I'm about private min margin) at this point...




回答8:


Try out this worked for me :

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

Set width of included layout android:layout_width="320dp". For devices with different screen size you can dynamically set the width of this included layout.




回答9:


You can use this. Inspired by this post, I've upgraded for the 5th edition. Because it was having problems with StatusBar in versions 5 and later.

you have to extend DrawerLayout and override some methods because MIN_DRAWER_MARGIN is private

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

}



回答10:


you can by below code

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



回答11:


<?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>

That's works perfectly for me. Hope help others.




回答12:


Google recommends having a maxim width of 320 dip as per the UI guidelines here. Moreover, the width can be set by specified the layout_width of the left_drawer ListView.




回答13:


<?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>




回答14:


You can also take a look at SlidingDrawer class. It's a deprecated class, but as the documentation says you can write your own implementation based on its source code.



来源:https://stackoverflow.com/questions/16754305/full-width-navigation-drawer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!