6

我有一个同时使用 SimpleOnScaleGestureListener 和 SimpleOnGestureListener 的应用程序。每当我进行缩放缩放时,我都会得到预期的onScale,但是当我抬起时,我看到一个奇怪的onScroll,它的起始位置是缩放缩放的开始位置,结束位置是缩放缩放的末尾。我的问题是,我可以防止伪造的 onScroll 吗?

这是代码:

@Override
public boolean onTouchEvent(MotionEvent event) {

    // Log every event.
    Log.d(TAG, Here.at() + String.format("Event: %d, Time: %d X: %f, Y: %f", 
            event.getAction(), 
            event.getEventTime(),
            event.getX(),
            event.getY()
            ));

    boolean handled = mScaleDetector.onTouchEvent(event); // This appears to ALWAYS return true (online reference indicated that's what the Android code does).

    handled |= mDetector.onTouchEvent(event);

    handled |= super.onTouchEvent(event);

    return handled;
}

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        // This is required.  If absent, the scale gesture never starts.
        Log.d(TAG, "In onScaleBegin");
        mIgnoreNextDrag = true;
        return true;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        Log.d(TAG, "In onScale");
        mTimeScale.doScale(detector.getScaleFactor(), detector.getFocusY());
        invalidate();
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        Log.d(TAG, "In onScaleEnd");
    }

}

private class GestureListener extends GestureDetector.SimpleOnGestureListener {

    @Override
    public boolean onScroll(MotionEvent me1, MotionEvent me2, float distanceX, float distanceY) {

        Log.d(TAG, String.format("Motion Event 1: %d, Time: %d X: %f, Y: %f", 
                me1.getAction(), 
                me1.getEventTime(),
                me1.getX(),
                me1.getY()
                ));

        Log.d(TAG, String.format("Event 2: %d, Time: %d X: %f, Y: %f", 
                me2.getAction(), 
                me2.getEventTime(),
                me2.getX(),
                me2.getY()
                ));


        return true;

    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent me) {
        // Do tap processing.
        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        // TODO: Future feature.
        return true;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        // This is required.  If absent, the scroll gesture never starts.
        return true;
    }

}

这是 LogCat:

13:06:05.885: D/my-tag(4140): In View.onTouchEvent, Event: 0, Time: 183279420 X: 171.761444, Y: 918.160767
13:06:05.895: D/my-tag(4140): In View.onTouchEvent, Event: 261, Time: 183279420 X: 171.761444, Y: 918.160767
13:06:05.895: D/my-tag(4140): In onScaleBegin
13:06:05.895: I/ScaleGestureDetector(4140): TwScaleGestureDetector
13:06:05.915: D/my-tag(4140): In View.onTouchEvent, Event: 2, Time: 183279458 X: 171.761444, Y: 908.474365
13:06:05.915: D/my-tag(4140): In onScale
13:06:06.015: D/my-tag(4140): In View.onTouchEvent, Event: 2, Time: 183279542 X: 174.964783, Y: 857.584717
13:06:06.015: D/my-tag(4140): In onScale
13:06:06.105: D/my-tag(4140): In View.onTouchEvent, Event: 2, Time: 183279641 X: 232.242096, Y: 731.365662
13:06:06.105: D/my-tag(4140): In onScale
13:06:06.215: D/my-tag(4140): In View.onTouchEvent, Event: 2, Time: 183279740 X: 313.564514, Y: 595.412964
13:06:06.215: D/my-tag(4140): In onScale
13:06:06.225: D/my-tag(4140): In View.onTouchEvent, Event: 6, Time: 183279751 X: 313.564514, Y: 595.412964
13:06:06.225: D/my-tag(4140): In onScaleEnd
13:06:06.245: D/my-tag(4140): In View.onTouchEvent, Event: 2, Time: 183279774 X: 333.316528, Y: 487.607422
13:06:06.245: D/my-tag(4140): In onScroll, me1: 0, Time: 183279420 X: 171.761444, Y: 918.160767
13:06:06.245: D/my-tag(4140): In onScroll, me2 2: 2, Time: 183279774 X: 333.316528, Y: 487.607422
13:06:06.255: D/my-tag(4140): In View.onTouchEvent, Event: 2, Time: 183279784 X: 331.539551, Y: 488.496460
13:06:06.265: D/my-tag(4140): In onScroll, me1: 0, Time: 183279420 X: 171.761444, Y: 918.160767
13:06:06.265: D/my-tag(4140): In onScroll, me2 2: 2, Time: 183279784 X: 331.539551, Y: 488.496460
13:06:06.275: D/my-tag(4140): In View.onTouchEvent, Event: 1, Time: 183279794 X: 331.539551, Y: 488.496460

您可以看到第一个事件是第一根手指向下 (0 = ACTION_DOWN),然后是第二根手指向下 (261 = ACTION_POINTER_2_DOWN)。然后我们看到来自 onScaleBegin 调用的日志条目和来自缩放手势检测器本身的日志(不是来自我的代码)。在这一点上,我想我可以放心地假设缩放手势已经开始了。这完全符合预期。

随后是四个移动事件 (2 = ACTION_MOVE),每个事件后面紧跟着来自 onScale 的日志条目。这仍然符合预期。

然后我们看到一个指针向上事件 (6 = ACTION_POINTER_UP),然后是来自 onScaleEnd 的日志条目,仍然 AOK!(请注意,它是 6 而不是 262,因为我抬起手指的顺序与放下手指的顺序相同,因此首先抬起的是指针 1,而不是指针 2。)

现在奇怪的一点。

我们看到一个移动事件,它被 SimpleOnGestureListener 中的 onScroll 拾取。第一个参数 me1 具有缩放手势开始之前第一个向下事件的 x 和 y 坐标。第二个参数 me2 具有明显反映缩放手势停止后的位置的坐标。

在这个例子中,实际上还有第二个移动事件,它也被解释为滚动手势,同样是预缩放原点。使用上面的代码,我会在捏缩放后得到 1、2 或没有滚动事件。

(为了结束 LogCat,我们为第二根手指设置了一个最终向上事件 (1 = ACTION_UP),并且日志变得安静。)

那我做错了吗?如果 SimpleOnScaleGestureListener 从 isInProgress 返回 false,我只尝试调用 SimpleOnGestureListener,但没有任何乐趣。

有任何想法吗?

在此先感谢,并感谢社区中的所有人,这些年来我从该站点获得的大量信息!

4

1 回答 1

7

我还发现这种特殊行为构建了具有平移/缩放功能的自定义视图。但经过一番思考,这是我的推理:

多点触控环境中,每个手指都被注册,它们各自的运动在某种并行分析中被处理。这就是允许检测系统可以通过OnGestureListener和发送的不同触摸事件的原因OnScaleGestureListener

对,没有什么你不知道的。

现在,考虑两个检测器的不同行为:

  • GestureDetector通过在视图的可视区域范围内仅用一根手指拖动来检测滚动事件。它的模式响应:向下 - 拖动 - 向上。当检测到拖动事件时会产生滚动事件。

  • ScaleGestureDetector通过在多点触控环境中两根手指同时触发两次拖动来检测缩放事件。它的模式响应:(down1&down2) - (drag1 和/或 drag2) - (up1 或 up2 )

现在,考虑一个自定义视图,您只需要检测滚动事件(忽略所有其他事件)。在这种情况下,滚动事件必须在所有其他考虑因素之外触发,因为您已经执行了它的模式(向下拖动)。

当你结合这两个检测器时,它们独立运行,所以缩放检测器首先触发,但是当你抬起第二根手指时,滚动检测器触发,因为它检测到一个手指正在拖动并完成一个向上事件!

结论:这种行为似乎是合理的……但 Android 可以为同时出现的情况提供一些交叉检测器。

好吧,您可以简单地输入一个布尔值并解决问题。我在我的实现中完成了这个:

  • 声明一个boolean命名scaling
  • 使您的 onDown 事件(在ACTION_DOWN事件上)清除scaling
  • 设置您的 onScale 事件scaling
  • scaling如果标志是,使您的 onScroll 事件不处理滚动true

这已经对我有用了。

于 2013-08-31T19:10:49.567 回答