【问题标题】:Why does DrawerLayout sometimes glitch upon opening?为什么 DrawerLayout 在打开时有时会出现故障?
【发布时间】:2013-07-27 14:11:13
【问题描述】:

我已按照教程Navigation Drawer 进行操作,除了一个小故障外,一切都像魅力一样。我会尽量解释清楚,如果还不清楚,我会尝试上传问题视频。

在尝试打开抽屉时会出现问题,并且仅在打开时才会出现,而且只是有时会发生,并非总是如此。那就是当我开始打开它时,它会在打开约 4 毫米时出现故障并冻结,并且始终保持相同的距离。如果我向后移动手指,它就不会继续打开或关闭,当我松开时,它会关闭。

请注意:

  • 我已在多台设备(Nexus 7、Nexus)和虚拟设备上尝试过,问题仍然存在。
  • Issue replicated using the example provided by Google. (Youtube Link)
  • Issue presented with my app. (Youtube Link)
  • 我设法在 Gmail 应用中复制了一次问题,但只在 Gmail 应用中复制了一次(我相信它使用相同的实现),但在我的应用和示例应用中出现的频率要高得多。
  • 我注意到,如果您只需单击屏幕边缘,就会在抽屉打开的相同距离处发生故障,因为它在初始打开后冻结并且从不拖动。

任何指针将不胜感激。

【问题讨论】:

  • 能否附上截图?
  • 如果您通过从边框滑动或按图标(假设您已实现此功能)或两种情况下都打开菜单会发生这种情况吗?您是否检查过 logcat 是否有任何异常?我从未见过这样的事情,我测试了大约 10 台设备。您使用的是哪个版本的支持库? p.s.:这个效果是否也出现在示例应用中? (见developer.android.com/training/implementing-navigation/…
  • @Trinimon 仅通过滑动发生。 Logcat 不显示任何内容。正在使用以前的支持库,并且刚刚做了最近发布的更新,它仍然会发生(版本 18)。如果您想查看,我已在原始问题中添加了一个视频。
  • 当且仅当故障出现D/InputEventConsistencyVerifier(870): TouchEvent: Source was not SOURCE_CLASS_POINTER. D/InputEventConsistencyVerifier(870): in android.widget.LinearLayout{40d10590 V.E..... ......I. 0,0-320,359} D/InputEventConsistencyVerifier(870): 0: sent at 58488550000000, MotionEvent { action=ACTION_CANCEL, ... 时,我会不断注意到以下 logcat 输出 ...但是,我找不到任何有助于解决此问题的方法 :(
  • 我注意到,如果您只需单击屏幕边缘,就会在抽屉打开的相同距离处发生故障,因为它在初始打开后冻结并且从不拖动。

标签: java android android-layout navigation-drawer


【解决方案1】:

我研究了DrawerLayout的代码,发现了下一个问题: 当我们触摸屏幕边缘时,drawer 的小部分(20*density px)出现(它使移动抽屉更容易)。它不会立即出现,而是在一定的时间间隔(160 毫秒)之后出现。由postDelayed实现。

drawer 可以处于多种状态:IDLEDRAGGINGSETTLING。如果它处于DRAGGING 状态,它就不能再以相同的指针和边缘返回此状态(因为有一个条件:mEdgeDragsInProgress[pointerId] & edge) == edge 不允许拖动已经拖动的边缘)。

所以在某些情况下,当延迟 Runnable 正在执行时,抽屉已经移动到状态 DRAGGING。此延迟操作打开drawer 20*密度像素并更改drawer 的状态。所以drawer不能再移动了(因为它不能回到DRAGGING的状态)。

有一个取消延迟操作的代码(打开抽屉),但是这个代码在方法onInterceptTouchEvent中,它只被调用一次(因为它返回false)。我认为这段代码应该在方法onTouchEvent中。

不幸的是,我没有找到任何方法来取消延迟事件(因为它有 private 修饰符,我无法得到它)。所以我只找到了一种方法:将DrawerLayout的源代码复制到我的项目中并进行这个小改动:复制

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

从方法onInterceptTouchEvent到方法onTouchEvent

【讨论】:

  • 伟大的研究,将其提升到新水平的道具。只是跟进,所以 DelayedRunnable 在执行时将状态更改为 SETTLING 然后 DRAGGING?或者只是到DRAGGING,但由于状态再次从DRAGGING更改为DRAGGING而出现故障?
  • 据我了解,状态DRAGGING - drawer 跟随手指,状态SETTLING - drawer 以恒定速度移动,例如当我们在中间释放它时,它会继续移动以关闭或打开)。 DelayedRunnable 将状态更改为SETTLING(因为它只是动画,drawer 不要跟随手指)。
  • 另一种解决方法是在主要内容 FrameLayout 中使用 android:clickable="true"。 stackoverflow.com/questions/18044277/…
  • @esentsov 我可能有同样的问题。我第一次打开抽屉时,它是逐步打开的,之后一切都很好。我尝试实现你所说的,但我不知道 mLeftDragger 或 mLeft/RightCallback 是谁。你能看看这里stackoverflow.com/questions/33713720/…吗?谢谢。
【解决方案2】:

drawerlayout 文件中没有错误。 只需将 ScrollView 作为父视图或根视图添加到 content.xml(setcontentview 文件) 文件中,然后 工具:context=".MainActivity"

【讨论】:

  • 这个问题已经有一个被接受的答案 - 在查看了其他答案之后,您真的应该只在您觉得您的答案增加了很多价值时才提交迟到的答案(例如使用最新的公用事业/图书馆看到问题是 3 岁)。
猜你喜欢
  • 2017-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 2012-06-25
相关资源
最近更新 更多