ScaleGestureDetector.onTouchEvent devuelve siempre 'true'

Pensé que el método "onTouchEvent ()" de una instancia de "ScaleGestureDetector" se suponía que devolvería "true" sólo si realmente maneja el evento táctil , es decir, si detecta un gesto de escalado multitáctil (con dos dedos). De lo contrario, pensé que se suponía que debía devolver "false" para permitir que otros manejadores manejaran el evento, por ejemplo, una pulsación larga para activar un menú contextual.

He observado algo diferente: scaleGestureDetector.onTouchEvent () siempre devuelve "true" en mi caso. El fragmento de código siguiente de mi clase 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); } 

produjo la salida de log siguiente después de tocar la vista durante aproximadamente 1 segundo con un dedo, luego realizó un gesto de escala con 2 dedos:

 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 

Como he dicho: ¡El valor de retorno es siempre "verdadero"! ¿Se trata de un error de ScaleGestureDetector.onTouchEvent ()? ¿Qué puedo hacer para que otros manejadores manejen todos los eventos sin escala (por ejemplo, presionar con un dedo)? ¡Por favor ayuda!

Nemax

No sé si es un error o intencional, pero eso es definitivamente lo que hace la fuente (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; } 

La forma en que resolví esto era para comprobar todos los otros tipos de eventos táctiles que podría querer manejar en primer lugar, a continuación, invocar el detector de gestos, por ejemplo

  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; } 

El ScaleGestureDetector proporciona el método isInProgress () que puede hacer lo que quiera …

He aquí un ejemplo de ello en uso:

 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; } 

Así resolví el problema: Anulando el método onDispatchTouchEvent () de Acitivity. Cualquier otra solución parecía no funcionar. Lo bueno del método onDispatchTouchEvent () es que siempre se llama antes de reenviar cualquier evento táctil a cualquier otro receptor, para que pueda interceptar cada evento de un solo toque aquí.

Si el evento se maneja en alguna parte de aquí (escala o desplazamiento), regreso inmediatamente sin reenviar el evento a la superclase, es decir, al resto de la jerarquía de vista. Si no lo es, lo remito a la superclase, por lo que otras vistas pueden manejarlo, por ejemplo, para detectar clics cortos o largos.

Había algunos más problemas a resolver sin embargo: 1. Si el usuario comienza un gesto de la escala, tuve que cancelar cualquier proceso largo de la detección del tecleo porque la visión que recibía conseguiría el primer acontecimiento ABAJO, después nada más (después del segundo dedo viene abajo y la escala comenzó), y entonces espuriosamente piensan que una prensa larga se está realizando. 2. Cuando se realizó una pulsación larga y el menú contextual surgió, tuve que evitar la detección de gestos de desplazamiento y escala aquí en dispatchOnTouchEvent () hasta el siguiente evento UP, de lo contrario se realizaría swiping y escalado aunque el menú contextual esté allí.

Bastante complicado, pero pasé horas y horas y mucho ensayo y error, y apenas no podría encontrar ninguna solución más simple. De todos modos, el manejo de 1. gestos de escala, 2. gestos de desplazamiento horizontal, 3. gestos de desplazamiento vertical, 4. clics largos y 5. clics cortos, todos en la misma vista de destino, no es una misión muy sencilla de lograr. .

Aquí está el código (partes relevantes del mismo):

  @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's 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, // ie 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; } 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.