Итак, проще говоря, обработчик, который я использовал для мультитач, аварийно завершал работу и выдавал «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.