13

I'm trying to implement the navigation drawer pattern based on my app. I downloaded the sample code from here and i ran it and 90 % of the times the drawer works ok, but sometimes the drawer gets stuck when i try to open it. I have a way of replicating the situation but it doesn't always work. What i do is:

1- Run the sample code as is.
2- Put your finger on the left edge to get the drawer peek
3- Let go of the finger and press it on the main fragment
4- Try to open the drawer as usual

Sometimes the drawer gets stuck on the peek mode no matter how much you swipe your finger to the right to open the drawer more. Has anyone had / fixed this issue?

xlar8or
  • 601
  • 9
  • 18
  • 2
    Possible duplicate of http://stackoverflow.com/questions/17896052/why-does-drawerlayout-sometimes-glitch-upon-opening/18086735#18086735 – paul Sep 16 '13 at 17:59

3 Answers3

20

I faced a similar issue as mentioned by you. I had a list view inside a relative layout (FILL_PARENT). Whenever the content in the list view is less and when I dragged in the area outside the list view, the navigation drawer got struck. Setting android:clickable="true" for the relative layout resolved the problem. Hope this may help.

nhahtdh
  • 55,989
  • 15
  • 126
  • 162
Viji
  • 201
  • 1
  • 5
  • You've set android:clickable="true" in the relative layout of the navigation drawer where you have the listview? – xlar8or Sep 14 '13 at 12:18
  • 2
    You need to set the `clickable="true"` to whatever layout you use to hold your main content. – mato May 25 '15 at 22:40
16

To clarify on Viji's answer, if you are using something like the navigation drawer example provided:

<!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
<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">

    <!-- As the main content view, the view below consumes the entire
         space available using match_parent in both dimensions. -->
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- android:layout_gravity="start" tells DrawerLayout to treat
         this as a sliding drawer on the left side for left-to-right
         languages and on the right side for right-to-left languages.
         The drawer is given a fixed width in dp and extends the full height of
         the container. A solid background is used for contrast
         with the content view. -->
    <ListView
        android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#111"/>
</android.support.v4.widget.DrawerLayout>

Adding android:clickable="true" to the FrameLayout seems to fix the problem.

Lawrr
  • 400
  • 5
  • 5
0

Two soluctions.

  1. setting android:clickable
  2. copy the source code of drawerlayout and delete peekDrawer function to disabled this feature

Let me explain why the reason of this bug.

DrawerLayout three states STATE_IDLE, STATE_DRAGGING, STATE_SETTLING

ACTION_DOWN-> onEdgeTouched will be triggered if it is on the edge, DrawerLayout will trigger peekDrawer after 120ms

PeekDrawer actually changes the state of DrawerLayout to STATE_SETTLING, and then lets the Drawer scroll to the specified location. Then set the state to IDLE.

ACTION_MOVE-> 

If the current state is DRAGGING, drag captureView

If the current state is not DRAGGING, it will try to execute tryCaptureViewForDrag to reset the state to DRAGGING.

And at the same time, it will also determine whether a new edge gesture is triggered (emphasis !!)

If a new edge gesture was dete, it will invoke onEdgeDragStared
and DrawerLayout will go to captureView to captrue the drawer

How the recuurent this bug?

  • First tap the edge to invoke a 120ms delay function peekDrawer
  • Before the peekDrawer was invoked, trigger onEdgeDragStared let the drawer layout capture the drawer, the dragState will be seted to STATE_DRAGGING
  • After 120ms, If your finger are still in the area that drawerLayout want to peek to , the dragState will be set to SETTLING
  • Before the drawer peek to the destination, move your finger fast out of that area before the peek end, the drawer will not follow your finger because the state is SETTLING, after SETTLING the state will be set to IDLE
  • Now your finger is totally out of the drawer and the state is IDLE , so you can’t drag the view anymore

So, the solution is to stop the peekDrawer actually, the drawerlayout has fixed this problem.

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (action) {

            case MotionEvent.ACTION_MOVE: {
                // If we cross the touch slop, don't perform the delayed peek for an edge touch.
                if (mLeftDragger.checkTouchSlop(ViewDragHelper.DIRECTION_ALL)) {
                    mLeftCallback.removeCallbacks();
                    mRightCallback.removeCallbacks();
                }
                break;
            }

        }

        return interceptForDrag || interceptForTap || hasPeekingDrawer() || mChildrenCanceledTouch;
    }

If the child is clickable, the child will consume the event, the onInterceptTouchEvent will invoke many times. and remove the peekDraw while move.

steven chan
  • 97
  • 2
  • 10