Получить MotionEvent.getRawX/getRawY из других указателей

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

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

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

5 ответов

Решение

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

}

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

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

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

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 в приведенном выше коде.

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

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);
}

Не существует 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;
}
Другие вопросы по тегам