Получить 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;
}