2

注意:我已更改此问题的原始标题。最初的标题是“如何一次使用多个 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

其中检测器是相同的。

4

1 回答 1

2

您可以使用复合设计模式创建一个将事件转发给其他侦听器的侦听器。

看看这些帖子:

将多个侦听器附加到android中的视图?

如何为一个事件设置多个侦听器?

于 2013-01-04T08:14:51.820 回答