Получить MotionEvent.getRawX / getRawY из других указателей - PullRequest
10 голосов
/ 29 июня 2011

Могу ли я получить значение MotionEvent.getRawX () / getRawY () других указателей?

Описание API MotionEvent.getRawX ()

API говорит, что использует getRawX / getRawY для получения исходной необработанной координаты X / Y, но это возможно только для 1 указателя (последний нажатый указатель), возможно ли получить необработанную координату X / Y другого указателя?

Ответы [ 5 ]

14 голосов
/ 28 марта 2012

Действительно, 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];

}
5 голосов
/ 14 марта 2015

Ответ getLocationOnScreen работает большую часть времени, но я видел, что он иногда возвращал неправильные значения (когда я переставлял и переопределял представление, когда происходило событие касания), поэтому я нашел альтернативный подход, который работает надежнее.

Если вы посмотрите на реализацию getRawX, она вызывает частную встроенную функцию, которая принимает pointerIndex, но класс MotionEvent только когда-либо вызывает ее с индексом 0:

public final float getRawX() {
    return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}

К сожалению, 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);
    }
}

Конечно, внутренние компоненты MotionEvent не задокументированы, поэтому этот подход может привести к сбою в прошлых или будущих версиях SDK, но, похоже, он мне подходит.

Редактировать: Похоже, тип mNativePtr и параметр nativePtr изменились с int до long на уровне API 20, поэтому, если вы нацеливаетесь на уровень 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.");
}

Затем я использовал NATIVE_GET_RAW_AXIS_VALUE вместо getRawAxisValueMethod в приведенном выше коде.

4 голосов
/ 03 июля 2013

Может быть недостаточно просто сдвигать локальные координаты по местоположению вида, если вид поворачивается. В этом случае вам нужно что-то вроде этого:

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);
}
2 голосов
/ 06 июля 2016

Решение, которое стоит попробовать в большинстве случаев, состоит в том, чтобы добавить это к первой строке onTouchEvent, оно просто находит разницу между необработанным и обработанным и смещает местоположение события MotionEvent на эту величину. Так что все значения getX (int) теперь являются необработанными значениями. Затем вы можете использовать getX () getY () в качестве необработанных значений.

event.offsetLocation(event.getRawX()-event.getX(),event.getRawY()-event.getY());

Несмотря на то, что точка зрения Ивана верна, это просто тот случай, когда применение матрицы непосредственно к самому представлению - отстой, так что вы, вероятно, не должны этого делать. Это странно и несовместимо между устройствами, приводит к тому, что сенсорные события выпадают из поля зрения и отклоняются и т. Д. Если вы перемещаете представление таким образом, вам лучше просто перегрузить onDraw () и применить эту матрицу к холсту, применяя обратную матрицу к MotionEvent, чтобы все было правильно. Тогда вы сможете правильно реагировать на события с должным и точным контролем зерна. И если вы сделаете это, мое решение здесь не будет предметом возражений Ивана.

0 голосов
/ 16 августа 2017

Нет API для получения специфичных для указателя RawX и RawY.Но вы можете вычислить аналогичные значения в отношении положения View к родителю и его поворота.Если родительский вид занимает всю сенсорную область, которую вы пытаетесь обработать, использование Matrix поможет вам решить вашу проблему:

private float[] calcRawCoords(MotionEvent event, int pointerIndex) {
    Matrix screenMatrix = new Matrix();
    screenMatrix.postRotate(getRotation(), mPivotX, mPivotY);
    screenMatrix.postTranslate(getLeft(), getTop());
    float viewToScreenCoords[] = {event.getX(pointerIndex), event.getY(pointerIndex)};
    screenMatrix.mapPoints(viewToScreenCoords);
    return viewToScreenCoords;
}
...