为什么DrawerLayout有时在打开时会出现故障?

27
我按照导航抽屉教程操作,除了一个小问题外一切正常。问题出现在尝试打开抽屉时,只有在打开时才会发生,且只是偶尔发生,并非总是发生。即当我开始打开抽屉时,它会出现故障和冻结,大约打开四毫米左右,总是相同的距离。然后,如果我向后移动手指,它将不会继续打开也无法关闭,当我松开手指时,它会关闭。
请注意: 欢迎提供任何指导。

1
请问您可以附上一张截图吗? - PravinCG
如果您通过从边缘滑动或按图标打开菜单(假设您已实现此功能),或者两种情况都会发生吗?您是否检查了logcat以查找任何异常?我从未见过这样的情况,并且我测试了大约10个设备。您使用的支持库版本是哪个?附言:这种影响是否也出现在示例应用程序中?(请参见http://developer.android.com/training/implementing-navigation/nav-drawer.html) - Trinimon
我注意到,如果你只是点击屏幕边缘,抽屉打开的距离相同,故障就会发生在那个距离上,也就是说,在初始打开后它会冻结,无法拖动。 - LuckyMe
对我有用:https://dev59.com/tWMl5IYBdhLWcg3w5KTd#18591274 - Perry_ml
这个 bug 最简单的解决方案在这里:https://dev59.com/-GMl5IYBdhLWcg3w5aaQ#20016088 - Slava
显示剩余2条评论
2个回答

27

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

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

因此,在某些情况下,当延迟的Runnable正在执行时,drawer已经移动到DRAGGING状态。这个延迟的动作打开了drawer 20*density px 并改变了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而出现故障? - LuckyMe
1
据我理解,状态DRAGGING- drawer跟随手指移动,状态SETTLING- drawer以恒定速度移动,例如当我们在中间释放它时,它会继续移动关闭或打开。DelayedRunnable将状态更改为SETTLING(因为这只是动画,drawer不再跟随手指移动)。 - esentsov
3
另一个解决方法是在主内容FrameLayout中使用android:clickable="true"。 - Cheok Yan Cheng
@esentsov我可能有同样的问题。第一次打开抽屉时,它会逐步打开,之后一切都正常和顺畅。我尝试了你说的方法,但是我不知道mLeftDragger或mLeft/RightCallback是什么。你能看看这个链接吗https://dev59.com/cJHea4cB1Zd3GeqPkhhO?谢谢。 - Bogdan Daniel

1
在drawerlayout文件中没有错误。只需将ScrollView作为content.xml(setcontentview文件)文件的父视图或根视图添加,并且tools:context=".MainActivity"。

1
这个问题已经有一个被接受的答案了 - 在查看其他答案后,只有当你觉得你的答案增加了很多价值时(例如使用最新的工具/库,因为问题已经有3年历史),你才应该提交一个晚回答。 - ishmaelMakitla

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