【问题标题】:Avoid coalescing of ACTION_MOVE MotionEvents in Android避免在 Android 中合并 ACTION_MOVE MotionEvents
【发布时间】:2012-10-06 11:42:12
【问题描述】:

我试图从 Android 中的 MotionEvents 序列中准确读取 ACTION_MOVE 增量。

然而,我用手指在屏幕上滑动的测试表明,在最初的ACTION_DOWN 之后,几个MotionEvents 不会传递给听众。只有在进行了足够大的移动之后,才会传递具有相对较大增量的ACTION_MOVE 事件。在第一次ACTION_MOVE 事件之后,所有增量都再次变得非常小。

或者,也可以触摸屏幕 (ACTION_DOWN) 并稍微移动一下,根本不会传递 ACTION_MOVE 事件。

我了解,对于许多用途,具有一定的移动(内)灵敏度是有益的。但是,有没有办法避免合并ACTION_MOVE 事件,从而交付每一个小的初始动作? ACTION_DOWN位置和第一个ACTION_MOVE位置之间的最小距离是否可以设置阈值?

这里是一些示例代码:

private float lastX;
private float lastY;

public boolean onTouch(View v, MotionEvent ev) {

    final float y = ev.getX();
    final float x = ev.getY();

    final int hs = ev.getHistorySize();

    final int ac = ev.getAction();
    switch(ac)
    {
        case MotionEvent.ACTION_DOWN:
        {
            this.lastX = x;
            this.lastY = y;
            Log.i("TEST", "Down at " + x + "/" + y + " (History size="+hs+")");
            return true;
        }
        case MotionEvent.ACTION_MOVE:
        {
            Log.i("TEST", "Move at " + x + "/" + y + ": Delta="+(lastX-x)+"/"+(lastY-y) + " (History size="+hs+")");
            this.lastX = x;
            this.lastY = y;
            return true;
        }
        case MotionEvent.ACTION_UP:
        {
            Log.i("TEST", "Up at " + x + "/" + y + " (History size="+hs+")");
            return true;
        }
    } 
}

以及对应的输出:

Down at 376.0/259.0 (History size=0)
Move at 392.0/268.0: Delta=-16.0/-9.0 (History size=0)
Move at 394.0/268.0: Delta=-2.0/0.0 (History size=0)
Move at 395.0/269.0: Delta=-1.0/-1.0 (History size=0)
Move at 394.0/268.0: Delta=1.0/1.0 (History size=0)
Move at 396.0/269.0: Delta=-2.0/-1.0 (History size=0)
Move at 399.0/271.0: Delta=-3.0/-2.0 (History size=1)
Move at 401.0/272.0: Delta=-2.0/-1.0 (History size=0)
Move at 403.0/273.0: Delta=-2.0/-1.0 (History size=0)
Move at 404.0/273.0: Delta=-1.0/0.0 (History size=0)
Move at 405.0/274.0: Delta=-1.0/-1.0 (History size=0)
Move at 406.0/275.0: Delta=-1.0/-1.0 (History size=0)
Up at 406.0/275.0 (History size=0)

换句话说,我想避免第二行 (-16/-9) 的大增量,但接收 1 或 2 的增量...

我在三星 Galaxy S2 和 Note 上进行了测试。

非常感谢任何帮助或建议!

【问题讨论】:

  • 是运动中的死区还是 ACTION_DOWN 后的死区 TIME,在此期间 ACTION_MOVE 被禁用?也许您可能想尝试找出哪个。
  • 使用 event.getHistoricalX/Y() 方法可以获得更多积分
  • 这绝对是运动中的死区。当 (1) 触摸 (ACTION_DOWN) 并保持手指稳定并在几秒钟后开始移动或 (2) 在屏幕上滑动而不等待时,可以观察到相同的行为。第一个ACTION_MOVE 事件始终具有最大的增量——好像在ACTION_MOVEACTION_DOWN 之后报告之前存在所需的最小增量

标签: java android motionevent


【解决方案1】:

我不想告诉你,但这完全是三星的错,只会发生在 Galaxy S2 上。基本上,他们无缘无故地在输入驱动程序中添加了软件触摸阈值。

我曾经试图说服他们取消门槛,但他们似乎没有理解问题并告诉我这是故意!尽管如此,他们还是在 Galaxy S3 中修复了它。

幸运的是,这不是硬件限制,所以如果你有一部已root的手机,或者安装一个自定义ROM,你就可以绕过它。

【讨论】:

  • 链接已损坏。
  • 抱歉,我从我的频道中删除了它,因为......人们还在使用 Galaxy S2s 吗?!
【解决方案2】:

我在一些应用程序中使用 MotionEvent 并且从未遇到过这个问题。

如果您使用的 SDK 版本 > 4,则 MotionEvent 支持多点触控。我正在我的应用程序上对此进行测试,并使用修改后的侦听器来获取触摸事件。代码如下:

private static PointF touchScreenStartPtArr[] = new PointF[10]; 
private static PointF touchScreenStopPtArr[] = new PointF[10]; 
private static PointF touchScreenCurrPtArr[] = new PointF[10]; 

OnTouchListener onTouchListenerMulti = new OnTouchListener() { 

    @Override 
    public boolean onTouch(View v, MotionEvent event) { 

        int action = event.getAction() & MotionEvent.ACTION_MASK; 
        int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
        int fingerId = event.getPointerId(pointerIndex); 

        switch (action) { 
        case MotionEvent.ACTION_DOWN: 
        case MotionEvent.ACTION_POINTER_DOWN: 
            touchScreenStartPtArr[fingerId].x = event.getX(pointerIndex); 
            touchScreenStartPtArr[fingerId].y = event.getY(pointerIndex); 
            break; 
        case MotionEvent.ACTION_UP: 
        case MotionEvent.ACTION_POINTER_UP: 
        case MotionEvent.ACTION_CANCEL: 
            touchScreenStopPtArr[fingerId].x = event.getX(pointerIndex); 
            touchScreenStopPtArr[fingerId].y  = event.getX(pointerIndex); 
            break; 
        case MotionEvent.ACTION_MOVE: 
            int pointerCount = event.getPointerCount(); 
            for (int i = 0; i < pointerCount; i++) { 
                touchScreenCurrPtArr[fingerId].x = event.getX(i); 
                touchScreenCurrPtArr[fingerId].y = event.getY(i); 
            } 
            break; 
        } 
        return true; 
    } 
}; 

--编辑--

在华为X5设备上运行代码的结果:

Down at 387.29388/247.27272 (History size=0)
Move at 387.29388/247.27272: Delta=0.0/0.0 (History size=0)
Move at 387.29388/247.27272: Delta=0.0/0.0 (History size=0)
Move at 387.29388/245.39589: Delta=0.0/1.876831 (History size=0)
Move at 387.29388/244.92668: Delta=0.0/0.46920776 (History size=0)
Move at 387.29388/244.45747: Delta=0.0/0.46920776 (History size=0)
Move at 387.29388/243.98827: Delta=0.0/0.46920776 (History size=0)
Move at 387.29388/243.51906: Delta=0.0/0.46920776 (History size=0)
Move at 389.01614/243.51906: Delta=-1.7222595/0.0 (History size=0)
Move at 389.8773/243.51906: Delta=-0.861145/0.0 (History size=0)
Move at 389.8773/243.04985: Delta=0.0/0.46920776 (History size=0)
Move at 390.73843/243.04985: Delta=-0.861145/0.0 (History size=0)
Move at 390.73843/242.58064: Delta=0.0/0.46920776 (History size=0)
Move at 391.59958/242.58064: Delta=-0.861145/0.0 (History size=0)
Move at 391.59958/242.11143: Delta=0.0/0.46920776 (History size=0)
Move at 392.46072/242.11143: Delta=-0.861145/0.0 (History size=0)
Move at 392.46072/241.64223: Delta=0.0/0.46920776 (History size=0)
Move at 393.32187/241.64223: Delta=-0.861145/0.0 (History size=0)
Move at 394.183/241.64223: Delta=-0.861145/0.0 (History size=0)
Move at 394.183/241.17302: Delta=0.0/0.46920776 (History size=0)
Move at 395.04413/241.17302: Delta=-0.8611145/0.0 (History size=0)
Move at 395.04413/242.11143: Delta=0.0/-0.9384155 (History size=0)
Move at 395.04413/242.58064: Delta=0.0/-0.46920776 (History size=0)
Up at 395.04413/242.58064 (History size=0)

看起来您可能有另一个侦听器从这个侦听器窃取事件。

我建议你在清理项目中检查你的代码(你需要在最后添加一个“return false”),以确认它。

祝你好运。

【讨论】:

  • 谢谢,路易斯。我理解多点触控的含义。但是,我在这里考虑一个简单的“单点触摸”场景,其中我只有一根手指,似乎在第一次触摸之后,在触发第一次移动事件之前所需的移动距离有一个最小阈值。我试图避免这种行为,即立即报告任何独立于阈值的移动。
  • @msto 你是对的,使用单点触控应该没有任何区别。我在模拟器和华为 x5 设备上都试过你的代码。它没有显示您报告的问题。我正在编辑答案以发布日志。
  • 路易斯,这确实很有趣!感谢您进行此测试。您在哪个 View 元素上注册了事件监听器?我使用了一个跨越整个视口的普通空视图。也许我必须稍微重命名这个问题并使其特定于三星手机......
  • 我使用了一个填充整个屏幕的 textView。我的设备正在运行 Gingerbread。
猜你喜欢
  • 2015-04-29
  • 1970-01-01
  • 2018-06-13
  • 2019-05-29
  • 2011-10-10
  • 1970-01-01
  • 2018-10-04
  • 2018-03-17
  • 1970-01-01
相关资源
最近更新 更多