ScaleGestureDetector.onTouchEvent всегда возвращает 'true'
Я думал, что метод "onTouchEvent()" экземпляра "ScaleGestureDetector" должен был вернуть "true" , только если он действительно обрабатывает событие touch, i. е. если он обнаруживает жест множественного касания (с двумя пальцами). В противном случае я утверждал, что он должен был возвращать "ложь", чтобы другие обработчики обрабатывали событие, например. г. длительное нажатие для запуска контекстного меню.
Я заметил что-то другое: scaleGestureDetector.onTouchEvent() всегда возвращает "true" в моем случае. Следующий фрагмент кода моего класса MyView:
public boolean onTouchEvent(MotionEvent event) {
boolean retval = scaleGestureDetector.onTouchEvent(event);
Log.v("MyView.onTouchEvent()", "Action: " + event.getAction() +
"; PointerCount: " + event.getPointerCount() +
"; scaleGestureDetector.onTouchEvent() RetVal: " + retval);
return(retval);
}
вывел вывод журнала после того, как я коснулся вида примерно на 1 секунду одним пальцем, затем выполнил масштабный жест с двумя пальцами:
01-01 19:09:54.484: VERBOSE/MyView.onTouchEvent()(5930): Action: 0; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.510: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.541: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.580: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.820: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.910: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:55.050: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:55.350: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:55.400: VERBOSE/MyView.onTouchEvent()(5930): Action: 1; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:57.160: INFO/BatteryStatsImpl(96): notePhoneSignalStrengthLocked: 4->3
01-01 19:10:00.060: ERROR/ClockWidget(215): weatherClock onReceive~ mUseAnimation:false
01-01 19:10:00.060: ERROR/ClockWidget(215): handleUiMessage~ in pause. msg:36867
01-01 19:10:00.070: ERROR/ClockWidget(215): weatherClock onReceive~ mUseAnimation:false
01-01 19:10:00.090: INFO/PI.Alarms(699): Update Alarms start
01-01 19:10:00.090: INFO/PI.Alarms(699): Task Notifications: Already displaying the same alarms, no update
01-01 19:10:00.100: INFO/PI.Alarms(699): Event Notifications: Already displaying the same alarms, no update
01-01 19:10:00.830: VERBOSE/MyView.onTouchEvent()(5930): Action: 0; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.840: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.870: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.900: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.922: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.931: VERBOSE/MyView.onTouchEvent()(5930): Action: 261; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.950: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.002: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.030: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.060: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.090: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.120: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.140: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.172: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.200: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.230: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.252: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.280: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.310: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.342: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.370: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.390: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.424: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.450: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.480: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.510: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.530: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.580: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.690: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.780: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.815: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.830: VERBOSE/MyView.onTouchEvent()(5930): Action: 262; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.840: VERBOSE/MyView.onTouchEvent()(5930): Action: 1; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
Как я уже сказал: возвращаемое значение всегда "истинно"! Является ли это ошибкой ScaleGestureDetector.onTouchEvent()? Что я могу сделать, чтобы другие обработчики обрабатывали все события, не связанные с шкалой (например, длительное нажатие одним пальцем)? Пожалуйста, помогите!
Nemax
Ответы
Ответ 1
Не знаю, является ли это ошибкой или преднамеренным, но определенно, что делает источник (ScaleGestureDetector.java:156):
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
boolean handled = true;
/* ... bunch of code that never updates 'handled' */
return handled;
}
Как я решил это, нужно проверить все другие типы событий касания, которые я, возможно, захочу обработать первым, а затем вызвать детектор жестов, например.
if (mLongPressGestureDetector != null && mLongPressGestureDetector.onTouchEvent(event))
return true;
else if (mIsInMoveMode && mScaleGestureDetector != null) {
// Check for a move
if (action == MotionEvent.ACTION_MOVE && !mScaleGestureDetector.isInProgress()) {
handleMove(event);
return true;
}
// Now a scale
mScaleGestureDetector.onTouchEvent(event);
return true;
}
Ответ 2
ScaleGestureDetector предоставляет метод isInProgress(), который может делать то, что вы хотите...
Вот пример использования:
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
if (!mScaleDetector.isInProgress()) {
if (event.getAction() == MotionEvent.ACTION_DOWN || (event.getAction() == MotionEvent.ACTION_MOVE)) {
touchX = (int) event.getX();
touchY = (int) event.getY();
isTouched = true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
isTouched = false;
}
} else {
isTouched = false;
}
return true;
}
Ответ 3
Вот как я решил проблему: переопределив метод Acitivity onDispatchTouchEvent(). Любое другое решение, похоже, не сработало. Хорошая вещь о методе onDispatchTouchEvent() - это всегда вызывать перед пересылкой любых событий касания к любому другому приемнику, поэтому вы можете перехватить каждое событие с одним касанием здесь.
Если событие обрабатывается где-то здесь (масштабирование или прокрутка), я возвращаюсь немедленно, не пересылая событие в суперкласс, т.е. е. к остальной иерархии представлений. Если это не так, я пересылаю его суперклассу, поэтому другие представления могут обрабатывать его, например. г. для обнаружения коротких или длинных кликов.
Было еще несколько проблем:
1. Если пользователь начинает жест масштаба, мне пришлось отменить любые процессы обнаружения длинных кликов, потому что приемное представление получит первое событие DOWN, а затем ничего больше (после того, как второй палец опустится и начнется масштабирование), а затем ложно подумайте что длительное нажатие выполняется. 2. Когда было выполнено длинное нажатие и появилось контекстное меню, мне пришлось предотвратить обнаружение жестов и масштабирования жестов здесь, в dispatchOnTouchEvent(), до следующего события UP, иначе прокрутка и масштабирование выполнялись бы даже при наличии контекстного меню.
Довольно сложно, но я потратил часы и часы и много проб и ошибок, и просто не мог найти более простого решения. Во всяком случае, обработка 1. жестов по шкале, 2. жесты горизонтального прокрутки, 3. вертикальные жесты прокрутки, 4. длинные клики и 5. короткие клики, все на одном и том же целевом представлении, не совсем простая миссия..
Вот код (соответствующие его части):
@Override
public boolean dispatchTouchEvent(MotionEvent e) {
if (eventInProgress) {
// View shall only receive scale gesture event if visible
if (targetView.isShown())
scaleGestureDetector.onTouchEvent(e);
if (scaleGestureDetector.isInProgress())
motionEventConsumed = true;
}
if (motionEventConsumed) {
if (e.getAction() == MotionEvent.ACTION_UP)
motionEventConsumed = false;
if (cancelLongPressEvent) {
cancelLongPressEvent = false;
targetView.cancelLongPress();
}
return (true);
}
// Get the action that was done on this touch event
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: {
// store the X value when the user finger was pressed down
downXValue = e.getX();
downYValue = e.getY();
cancelLongPressEvent = true;
eventInProgress = true;
break;
}
case MotionEvent.ACTION_MOVE:
// When having moved by too many x or y pixels, then
// cancel any ongoing long klick events
if (cancelLongPressEvent
&& Math.abs(e.getX() - downXValue)
+ Math.abs(e.getY() - downYValue) > 40) {
targetView.cancelLongPress();
cancelLongPressEvent = false;
}
break;
case MotionEvent.ACTION_UP: {
if (eventInProgress) {
// Get the X value when the user released his/her finger
float deltaX = e.getX() - downXValue;
float deltaY = e.getY() - downYValue;
if (Math.abs(deltaX) > Math.abs(deltaY)
&& Math.abs(deltaX) > 50) {
// going backwards: pushing stuff to the right
if (deltaX > 0) {
flipRight();
return (true);
}
// going forwards: pushing stuff to the left
if (deltaX < 0) {
flipLeft();
return (true);
}
break;
}
}
}
}
// If event was not handled here, then forward it to parent,
// i. e. to view hierarchy
return (super.dispatchTouchEvent(e));
}
[...]
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater mi = getMenuInflater();
mi.inflate(R.menu.lztv_context_menu, menu);
contextMenuTargetView = v;
eventInProgress = false;
}