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