Android导航抽屉使用示例时出现的错误

13

我正在尝试基于我的应用程序实现导航抽屉模式。我从 这里下载了示例代码并运行它,90%的情况下抽屉正常工作,但有时在尝试打开抽屉时抽屉会被卡住。我有一种复制情况的方法,但并不总是有效。我的操作步骤如下:

1- 按原样运行示例代码。
2- 将手指放在左侧边缘以获取抽屉提示
3- 放开手指并在主片段上按下手指
4- 尝试像往常一样打开抽屉

有时无论您将手指向右滑动多少次以打开抽屉,抽屉始终会卡在提示模式上。有没有人遇到/修复这个问题?


2
可能是 https://dev59.com/imMm5IYBdhLWcg3wFLw4#18086735 的重复问题。 - paul
3个回答

20

我遇到了一个和你提到的类似的问题。我在一个相对布局 (FILL_PARENT) 中放置了一个列表视图。每当列表视图中的内容较少,当我拖动列表视图外部的区域时,导航抽屉就会卡住。为相对布局设置 android:clickable="true" 可以解决这个问题。希望这可以帮到你。


你在导航抽屉的相对布局中设置了android:clickable="true",其中包含了listview吗? - xlar8or
2
你需要将 clickable="true" 设置到你用于承载主要内容的任何布局中。 - mato

16

为了澄清Viji的回答,如果您正在使用类似提供的导航抽屉示例的东西:

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

在FrameLayout中添加android:clickable="true"似乎可以解决这个问题。


0

两种解决方案。

  1. 设置 android:clickable
  2. 复制 DrawerLayout 的源代码并删除 peekDrawer 函数以禁用此功能

让我解释一下这个 bug 的原因。

DrawerLayout 有三种状态: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实际上会改变DrawerLayout的状态为STATE_SETTLING,然后让抽屉滚动到指定位置。然后将状态设置为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

如何解决这个问题?

  • 首先点击边缘以调用一个120毫秒的延迟函数peekDrawer
  • 在调用peekDrawer之前,触发onEdgeDragStarted事件让抽屉布局捕获抽屉,此时dragState将被设置为STATE_DRAGGING
  • 如果你的手指仍然在抽屉布局想要查看的区域内,在120毫秒后,dragState将被设置为SETTLING
  • 在抽屉查看到目的地之前,快速将手指移出那个区域,抽屉不会跟随你的手指,因为状态是SETTLING,SETTLING完成后状态将被设置为IDLE
  • 现在你的手指完全离开抽屉,状态为IDLE,所以不能再拖动视图了

因此,解决方案就是停止peekDrawer,实际上,抽屉布局已经解决了这个问题。

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


如果子元素是可点击的,那么子元素将会消耗事件,onInterceptTouchEvent 将会被多次调用。同时在移动时移除 peekDraw。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接