Android: получение правильного идентификатора указателя для приложений multiTouch на Android

Итак, проще говоря, обработчик, который я использовал для мультитач, аварийно завершал работу и выдавал «IllegalArgumentException: pointerIndex вне допустимого диапазона».

Я попытался просто поймать ошибку, но потом понял, что (x, y) по умолчанию будет (min.x, max.y), что в моем случае было (0,0, 480,0).

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

Вот метод onTouch:

    public boolean onTouch(View v, MotionEvent event) {
    synchronized (this) {
        int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        int pointerId = event.getPointerId(pointerIndex);
        TouchEvent touchEvent;

        // pointerId fix
        if (pointerId >= event.getPointerCount() && pointerId > 0)
            pointerId--;

        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_POINTER_DOWN:
            touchEvent = touchEventPool.newObject();
            touchEvent.type = TouchEvent.TOUCH_DOWN;
            touchEvent.pointer = pointerId;
            try {
                touchEvent.x = touchX[pointerId] = (int) (event
                        .getX(pointerId) * scaleX);
                touchEvent.y = touchY[pointerId] = (int) (event
                        .getY(pointerId) * scaleY);
            } catch (IllegalArgumentException e) {
                Log.d("multiTouchHandler", e.toString() + ":: PID = "
                        + pointerId);
            }
            isTouched[pointerId] = true;
            touchEventsBuffer.add(touchEvent);
            break;

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_POINTER_UP:
            touchEvent = touchEventPool.newObject();
            touchEvent.type = TouchEvent.TOUCH_UP;
            touchEvent.pointer = pointerId;

            try {
                touchEvent.x = touchX[pointerId] = (int) (event
                        .getX(pointerIndex) * scaleX);
                touchEvent.y = touchY[pointerId] = (int) (event
                        .getY(pointerIndex) * scaleY);
            } catch (IllegalArgumentException e) {
                Log.d("multiTouchHandler", e.toString() + ":: PID = "
                        + pointerId);
            }
            isTouched[pointerId] = false;
            touchEventsBuffer.add(touchEvent);
            return false;

        case MotionEvent.ACTION_MOVE:
            int pointerCount = event.getPointerCount();
            for (int i = 0; i < pointerCount; i++) {
                touchEvent = touchEventPool.newObject();
                touchEvent.type = TouchEvent.TOUCH_DRAGGED;
                touchEvent.pointer = pointerId;
                try {
                    touchEvent.x = touchX[pointerId] = (int) (event
                            .getX(pointerIndex) * scaleX);
                    touchEvent.y = touchY[pointerId] = (int) (event
                            .getY(pointerIndex) * scaleY);
                } catch (IllegalArgumentException e) {
                    Log.d("multiTouchHandler", e.toString() + ":: PID = "
                            + pointerId);
                }
                isTouched[pointerId] = false;
                touchEventsBuffer.add(touchEvent);
            }
            break;
        case MotionEvent.ACTION_CANCEL:
        }
        return true;
    }
}

Из вывода Log.d казалось, что происходит то, что иногда pointerId назначался следующему доступному указателю (например, 1), но фактически данные указателя сохранялись в предыдущем местоположении указателя (0).

Проверив, находится ли pointerId за пределами текущего диапазона pointerCount, и если да, вернувшись на один, я смог, по крайней мере, решить эту проблему для двух пальцев с помощью двух строк кода.

// pointerId fix
if (pointerId >= event.getPointerCount() && pointerId > 0)
                pointerId--;

Я не уверен, действительно ли это решает проблему, но кажется, что это отлично работает для мультитач-управления двумя пальцами, такого как те, которые используются в играх fps с сенсорным экраном. Мои собственные тесты показали, что он смог точно определить касание с обеих точек без исключений и сохранить правильные значения (x, y)

Есть ли лучшее решение, или API мультитач использует идентификаторы указателей так плохо? Если кто хочет знать, это было на устройстве 4.0.3.


person cavemaneca    schedule 04.04.2012    source источник
comment
Есть ли лучший способ решить эту проблему? Например, есть ли фундаментальная проблема с моим кодом? Или это ошибка во всем мультитач-коде, которую можно исправить только таким обходным путем?   -  person cavemaneca    schedule 11.04.2012


Ответы (1)


Мне кажется странным, что вы обращаетесь к event.getX() и event.getY() с pointerId в качестве аргумента (в ACTION_POINTER_DOWN). Я думаю, что это должен быть pointerIndex вместо этого, как и во всех других случаях ниже.

person Baszenski    schedule 01.06.2014