两种解决方案。
- 设置
android:clickable
- 复制drawerlayout的源代码并删除peekDrawer功能以禁用该功能
让我解释一下为什么会出现这个错误。
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,然后让 Drawer 滚动到指定位置。然后将状态设置为 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
这个bug怎么重现?
- 首先点击边缘调用 120ms 延迟函数
peekDrawer
- 在调用 peekDrawer 之前,触发 onEdgeDragStared 让抽屉布局捕获抽屉,dragState 将设置为 STATE_DRAGGING
- 120ms后,如果手指还在drawerLayout想要查看的区域,dragState会被设置为SETTLING
- 在drawer peek到目的地之前,将手指快速移出peek结束前的那个区域,drawer不会跟随你的手指,因为状态是SETTLING,SETTLING后状态将设置为IDLE
- 现在你的手指完全离开了抽屉,状态是 IDLE ,所以你不能再拖动视图了
所以,解决方案是停止 peekDrawer
实际上,drawerlayout 已经解决了这个问题。
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。