获取其他指针的MotionEvent.getRawX/getRawY

LZN*_*LZN 10 android multi-touch

我可以获得其他指针的MotionEvent.getRawX()/ getRawY()的值吗?

MotionEvent.getRawX()api引用

api说使用getRawX/getRawY来获取原始的原始X/Y坐标,但它仅用于1个指针(最后一个触摸指针),是否可以获得其他指针的原始X/Y坐标?

Séb*_*ZAT 14

实际上,API不允许这样做,但您可以计算它.试试看:

public boolean onTouch(final View v, final MotionEvent event) {

    int rawX, rawY;
    final int actionIndex = event.getAction() >> MotionEvent.ACTION_POINTER_ID_SHIFT;
    final int location[] = { 0, 0 };
    v.getLocationOnScreen(location);
    rawX = (int) event.getX(actionIndex) + location[0];
    rawY = (int) event.getY(actionIndex) + location[1];

}
Run Code Online (Sandbox Code Playgroud)


Tat*_*ize 6

对于大多数用例,值得尝试的解决方案是将其添加到它的第一行,onTouchEvent它只是找到原始和处理之间的差异,并将 MotionEvent 事件的位置移动该量。这样所有的 getX(int) 值现在都是原始值。然后你实际上可以使用 getX() getY() 东西作为原始值。

event.offsetLocation(event.getRawX()-event.getX(),event.getRawY()-event.getY());
Run Code Online (Sandbox Code Playgroud)

虽然 Ivan 的观点是有效的,但只是将矩阵直接应用于视图本身很糟糕,您可能不应该这样做。设备之间很奇怪且不一致,导致触摸事件从视图中消失并被拒绝等。将逆矩阵应用到 MotionEvent 以便一切都正确地网格化。然后,您可以通过适当和精细的粒度控制对事件做出正确的反应。而且,如果你这样做,我在这里的解决方案就不会受到 Ivan 的反对。

  • 比其他解决方案简单得多,并注意重载 onDraw()。尝试将接触点映射到转换后的视图是令人头疼的问题。 (2认同)

Iva*_*rov 5

如果视图旋转,仅通过视图位置移动局部坐标可能是不够的。在这种情况下,您需要这样的东西:

void getRowPoint(MotionEvent ev, int index, PointF point){
    final int location[] = { 0, 0 };
    getLocationOnScreen(location);

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

    double angle=Math.toDegrees(Math.atan2(y, x));
    angle+=getRotation();

    final float length=PointF.length(x,y);

    x=(float)(length*Math.cos(Math.toRadians(angle)))+location[0];
    y=(float)(length*Math.sin(Math.toRadians(angle)))+location[1];

    point.set(x,y);
}
Run Code Online (Sandbox Code Playgroud)


Ala*_*rce 5

getLocationOnScreen回答工作的大部分时间,但我看到它有时会返回不正确的值(当我重新定位和重新养育的观点而触摸事件正在发生),所以我发现,更可靠地工作的另一种方法.

如果你看一下getRawX它的实现,它会调用一个接受a的私有本机函数pointerIndex,但是MotionEvent类只能用索引0来调用它:

public final float getRawX() {
    return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,它nativeGetRawAxisValue是私有的,但你可以通过使用反射让自己访问所需的一切来解决这个问题.这是代码的样子:

private Point getRawCoords(MotionEvent event, int pointerIndex) {
    try {
        Method getRawAxisValueMethod = MotionEvent.class.getDeclaredMethod(
                "nativeGetRawAxisValue", long.class, int.class, int.class, int.class);
        Field nativePtrField = MotionEvent.class.getDeclaredField("mNativePtr");
        Field historyCurrentField = MotionEvent.class.getDeclaredField("HISTORY_CURRENT");
        getRawAxisValueMethod.setAccessible(true);
        nativePtrField.setAccessible(true);
        historyCurrentField.setAccessible(true);

        float x = (float) getRawAxisValueMethod.invoke(null, nativePtrField.get(event),
                MotionEvent.AXIS_X, pointerIndex, historyCurrentField.get(null));
        float y = (float) getRawAxisValueMethod.invoke(null, nativePtrField.get(event),
                MotionEvent.AXIS_Y, pointerIndex, historyCurrentField.get(null));
        return new Point((int)x, (int)y);
    } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException|
            NoSuchFieldException e) {
        throw new RuntimeException(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,MotionEvent内部没有记录,所以这种方法可能会崩溃过去或未来版本的SDK,但它似乎对我有用.

编辑:看起来类型mNativePtrnativePtrparam从API级别20 更改intlong,所以如果你的目标是API级别19或更早,上面的代码将崩溃,因为getDeclaredMethod找不到任何东西.要在我的代码中解决这个问题,我只是通过名称而不是完整类型签名来获取方法,这恰好在这种情况下起作用.没有办法直接查找具有给定名称的方法,因此我在静态初始化时循环通过声明的方法并将匹配的方法保存到静态字段.这是代码:

private static final Method NATIVE_GET_RAW_AXIS_VALUE = getNativeGetRawAxisValue();
private static Method getNativeGetRawAxisValue() {
    for (Method method : MotionEvent.class.getDeclaredMethods()) {
        if (method.getName().equals("nativeGetRawAxisValue")) {
            method.setAccessible(true);
            return method;
        }
    }
    throw new RuntimeException("nativeGetRawAxisValue method not found.");
}
Run Code Online (Sandbox Code Playgroud)

然后我用NATIVE_GET_RAW_AXIS_VALUE代替getRawAxisValueMethod上面的代码.