注意:我已更改此问题的原始标题。最初的标题是“如何一次使用多个 GestureDetector 实例?”,但我发现问题与多个检测器无关,而是与视图的层次结构有关。剩下的问题保持原样,我决定不删除任何内容,只需通读即可。请建议我是否应该扔掉过时的零件,我已经准备好这样做,但不确定是否应该这样做。
我在另一个(容器)中有一个视图(按钮),我希望他们做两件事:1)如果用户滚动任何视图(点击并移动),整个容器都会滚动。2) 如果用户单击了按钮,则为该按钮调用单击处理程序。
我的容器不是一个简单的视图,它允许双向滚动(这里是细节),所以我设置了一个检测滚动事件并滚动容器的 GestureDetector,感谢323go的建议。此检测器从附加到主容器视图的 OnTouchListener 的 onTouch 方法中调用。
我的问题是按钮。
如果我设置 onClick 处理程序,它似乎会完全接收所有运动事件,所以如果我开始滚动按钮,我将无法滚动视图。
好的,也许我可以在按钮上设置另一个 GestureDetector 以便它处理点击和滚动?
不幸的是,这不起作用。按钮的 onScroll 处理程序在 distanceX 和 distanceY 参数中接收疯狂的值。看起来容器视图和按钮都有自己的滚动计数器,所以当我滚动视图时,会为按钮累积一些偏移量。
所以我的问题是:使用 GestureDetector 类的预期方式是什么?我可以在视图层次结构中使用多个检测器,是否可以将一个检测器的处理委托给另一个检测器?即,如果子检测器返回 false,则父检测器可以处理该事件。
补充:事实上,我很喜欢这样简单的“父子”解决方案:只有一个 GestureDetector 附加到层次结构中的最顶层视图,它接收所有事件并决定如何处理它们,所以如果它检测到滚动——它只是滚动整个视图并且不通知任何人,但如果它检测到点击——它会在点击位置下方找到子视图并将点击转发给它。但这是真正的 Android方式吗?这甚至可能吗?
进一步调查:我尝试遵循下面评论中给出的建议,并让我的子手势检测器从其 onScroll 方法返回 false。这没有帮助:如果子处理程序返回 false,则根本不会调用父处理程序。
但我注意到,如果子处理程序不尝试滚动父视图,它将收到正确的值。
这是我的处理程序的代码:
private class ParentHandler extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.d("UI", "Parent Scroll X: " + String.valueOf(distanceX) + " Y: " + String.valueOf(distanceY));
scrollBy((int)distanceX, (int)distanceY);
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {return true;}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {return true;}
@Override
public boolean onDown(MotionEvent e) {return true;}
};
private class ChildHandler extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.d("UI", "Child Scroll X: " + String.valueOf(distanceX) + " Y: " + String.valueOf(distanceY));
// The line below drives the handler crazy!
// But if I remove it, the view will not be scrolled at all
// because the parent handler is not called
// even if this handler returned false.
scrollBy((int)distanceX, (int)distanceY);
return false;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {return true;}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {return true;}
@Override
public boolean onDown(MotionEvent e) {return true;}
};
请注意第二个处理程序中的注释。
如果删除了疯狂的呼叫,这是我在日志中得到的内容:
Parent Scroll X: -1.0 Y: 2.0
Parent Scroll X: 0.0 Y: 2.0
Parent Scroll X: -1.0 Y: 1.0
Parent Scroll X: -1.0 Y: 2.0
Parent Scroll X: 0.0 Y: 2.0
...
Child Scroll X: -1.0 Y: 2.0
Child Scroll X: 0.0 Y: 2.0
Child Scroll X: -1.0 Y: 1.0
Child Scroll X: -1.0 Y: 2.0
Child Scroll X: 0.0 Y: 2.0
但是,一旦我尝试在子处理程序中移动视图,值就会变成这样:
Child Scroll X: 13.0 Y: 22.0
Child Scroll X: -11.0 Y: -15.9999695
Child Scroll X: 11.0 Y: 17.00003
Child Scroll X: -10.0 Y: -16.00003
Child Scroll X: 11.0 Y: 19.99997
请注意,值是交替的:13 之后是 -11,然后是 11,然后是 -10,依此类推。所以平均漂移似乎没问题,但是如果我只是将这些值传递给 scrollBy 调用,则视图会在我滚动它的时候一直抖动和跳来跳去。
显然,在为子视图触发的 onScroll 中调用 scrollBy 存在一些问题。如果从父处理程序执行相同的调用,则没有问题。
那里可能有什么问题?
补充:我已经删除了附加到父级的 GestureDetector,但行为是相同的。
所以问题其实如下。当 GestureDetector 检测到滚动时,我需要滚动整个视图。要滚动视图,我调用scrollBy。如果为父视图触发原始 onTouch 事件,这确实有效,但如果为子视图触发 onTouch 则不会:在后一种情况下,尝试滚动父视图会影响 GestureHandler 接收的值。
所以这有效:
parent.onTouch -> detection.onTouchEvent -> parent.scrollBy
这不会:
child.onTouch -> detection.onTouchEvent -> parent.scrollBy
其中检测器是相同的。