如何从双指多点触控事件中获取旋转角度?

8

我有一个 ImageView,可以使用一根手指拖动或两个手指缩放,这很好用。我已经扩展了它来处理旋转,但是它的行为引起了一些混淆。

当出现多点触摸事件时,将调用此方法,应返回旋转角度(以度为单位)。它没有做我期望的事情。

private int fingersAngle(MotionEvent event)
{
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    int degrees = (int)(Math.toDegrees(Math.atan2(y, x)));
    return degrees;
}

当我旋转这两个手指时,我期望的输出是:
158 166 168 169 174 176 179 181 等等
但实际上我得到的却像这样:
158 166 -179 179 -179 179 -179
问题似乎出在符号上。这个方法如何知道它是180还是-180,或者是90还是-270呢?图像经常旋转错误的方向,然后突然跳转并开始旋转相反的方向。更糟糕的是,它最初旋转的方向实际上是随机的。
我正在使用Nexus One测试应用程序,但在Advent Vega平板电脑上也遇到了同样的问题。这两个设备都可以使用Google Maps中的3D功能旋转屏幕(有时会有点抖动),因此证据并不表明存在硬件限制。
一个次要的问题是,当这两个手指大致垂直或水平对齐时,角度根本不会改变,因此旋转会“卡住”约10-20度的顶部/底部/左侧/右侧。
目前,我正在检查角度是否突然发生了巨大变化,如果是,就从360中减去它。虽然很丑陋,但有点帮助。
希望你们中的一位以前见过这种情况,并知道如何从多点触摸手势中提取角度旋转。
在Android中有些东西非常容易,令人惊讶的简单,但像这样的东西却非常痛苦。

我对此一无所知,但如果你只有两个点,如何能了解旋转的情况呢?旋转必须相对于某个点才行,不是吗?难道你不需要知道运动开始时的坐标(根据Andriod文档中的ACTION_DOWN),然后计算它们之间的中心点,然后当你获得描述后续移动的运动事件(并在手指抬起之前(根据Andriod文档中的ACTION_MOVE)),你就可以计算出围绕原始中心点的旋转...或类似的东西... - Sam Holder
我正在测量相对于水平轴和拖动起点之间角度的旋转。 - Ollie C
@OllieC,这个问题对你有帮助吗?(https://dev59.com/ZFHTa4cB1Zd3GeqPSpTK) - Sam Holder
我的建议是编写单元测试,您可以使用已知的值来进行测试并断言您期望的结果,然后您可以确保在使用触摸事件之前正确地运行函数。 - Sam Holder
不这么认为。据我所知,ImageView旋转在-180到180度的角度范围内运作良好。我尝试将其加上180度,但没有任何区别。 - Ollie C
显示剩余5条评论
4个回答

4
为使这项工作像用户预期的那样运作,您必须保留一些状态。
弧正切仅告诉您两个点之间的角度-系统的瞬时快照。但是您正在执行动画-即随时间发生的事情。
因此,我认为您的“丑陋”解决方案尝试检测角度的大变化是正确的轨道。您真正应该随着时间推移跟踪每个手指的位置。
假设用户使用拇指和食指进行手势操作。您需要确保他们的数字始终得到一致的表示,例如拇指始终由(x0,y0)表示,食指由(x1,y1)表示。
设备可能具有某些规则来报告每个坐标。例如,也许它总是在事件的“0”插槽中报告左上坐标,在“1”插槽中报告右下坐标。但是,如果用户将拇指从右下移动到左上,则必须确保您仍将其视为(x1,y1)在角度公式中处理,即使设备现在将其报告为(x0,y0)在事件中。否则,图像将快速翻转180度,来回反复。
如果设备不为您跟踪手指,则必须想出一个启发式算法来猜测。一个简单的“哪个先前坐标更接近当前坐标”的算法是一个很好的起点,但如果事件的分辨率很差,则可能需要更高级别的算法。

我遇到了同样的问题。你能帮我解决这个问题吗? - Sabby

1

我从未遇到过这个问题,但我的猜测是使用 atan2 强制将您的角度从 -180 到 +180。

但是,您想要应用于图像的旋转应该从 0 到 360。

最简单的解决方案可能是在返回变量之前将您的度数变量加上 180。

编辑:由于您在评论中添加了随机性,我认为根据您使用的设备,您可能会遇到臭名昭著的 HTC(和其他早期)多点触摸错误:http://www.youtube.com/watch?v=Ds5qZ_3XRzI 这显示了 Nexus One 的情况,但 Hero 和一些 Motorola 也有同样的问题。


我编辑了我的答案,添加了关于您似乎得到的随机性的部分。 - Yahel

0

atan2()函数返回的值在范围(-180..+180)内。

鉴于此,现在看起来它是否正常工作了?

(如果你处于+179度,再旋转三度将会是+182度;但是这被表示为-178度)


我不确定这是否有任何区别。问题并不在于返回值来自哪个范围,而是它一直在变化(并且“粘”在四个轴上)。 - Ollie C
很难评估它在做什么,因为行为具有随机元素,无论是拖动开始时(哪个方向),还是在拖动过程中粘滞和跳跃。很难看清发生了什么。 - Ollie C

0

检查您是否能够同时并完全独立地读取2个点。我不确定 Nexus One 是否可以。请查看此页面:

http://developer.android.com/reference/android/content/pm/PackageManager.html

并查找您的目标设备是否仅支持FEATURE_TOUCHSCREEN_MULTITOUCH或更好(即FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)。最简单的方法可能是安装一个测试应用程序-我认为我使用了“多点触摸可见测试”。

如果您的设备不支持,则没有修复或解决方法。这是驱动程序限制,通常是由于吝啬的制造商选择劣质触摸屏引起的。


正如我之前所说,我可以在Nexus One上的Google Maps中进行双指旋转,因此该设备支持此功能。有时会有些卡顿,但它确实可以工作。同样适用于平板电脑,尽管便宜,但比Nexus One更好用。但是我的应用程序在两者上都无法正常工作。 - Ollie C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接