9

Tengo una vista personalizada con mapas de bits que el usuario puede arrastrar.Detener que OnLongClickListener se dispare mientras arrastra

Quiero que sea tan larga cuando se haga clic en uno de ellos me puede hacer aparecer un menú contextual con opciones como la posición de reposición, etc.

En la costumbre Ver agrego mi OnLongClickListener:

this.setOnLongClickListener(new View.OnLongClickListener() { 
    @Override 
    public boolean onLongClick(View v) { 
     // show context menu.. 
     return true; 
    } 
}); 

y anular onTouchEvent a ser algo como esto:

función
public boolean onTouchEvent(MotionEvent event) { 
    handleDrag(event); 
    super.onTouchEvent(event); 
    return true; 
} 

El handleDrag encuentra qué objeto se ha pulsado, y se ocupa de actualizar su posición.

Mi problema es que cuando comienzo a arrastrar una imagen, el OnLongClickListener también se dispara. No estoy seguro de la mejor manera de evitar esto.

He intentado agregar un umbral para handleDrag para que devuelva falso si el usuario toca pero no intenta arrastrarlo, pero me resulta todavía más difícil obtener el controlador correcto disparado.

¿Alguien puede sugerir una forma de omitir el OnLongClickListener mientras arrastra?

Respuesta

7

Creo que tengo esto resuelto a través de ajustar mi enfoque de umbral.

En primer lugar, he cambiado de onTouchEvent a tener este aspecto:

public boolean onTouchEvent(MotionEvent event) { 
    mMultiTouchController.handleDrag(event); 
    return super.onTouchEvent(event); 
} 

Ahora tanto el fuego, por lo que luego cambié OnLongClickListener a lo siguiente:

this.setOnLongClickListener(new View.OnLongClickListener() { 
    @Override 
    public boolean onLongClick(View v) { 
     if (!mMultiTouchController.has_moved) { 
      // Pop menu and done... 
      return false; 
     } 
     return true; 
    } 
}); 

(mMultiTouchController es la clase que contiene todo mi código de detección de gestos). La clave aquí está dentro de esta clase, agregué el bool 'has_moved'. Cuando voy a empezar un lastre que después calcular el delta:

float diffX = Math.abs(mCurrPtX - mPrevPt.getX()); 
float diffY = Math.abs(mCurrPtY - mPrevPt.getY()); 
if (diffX < threshold && diffY < threshold) { 
    has_moved = false; 
    return; 
} 

Ahora, cuando los incendios onLongClick sé si ha de proceder o no.

La pieza final fue establecer:

setHapticFeedbackEnabled(false); 

en mi opinión, de manera que el usuario no obtiene una vibración cada vez que los fuegos longClick pero no se toman medidas. Planeo hacer la vibración manualmente como un siguiente paso.

Esto parece estar bien hasta ahora, espero que ayude a cualquiera que haya encontrado una situación similar a esta.

3

Dejo de usar el onLongClickListener y simplemente implemento el suyo, lo cual es bastante fácil de hacer. Entonces usted tiene el control que necesita para evitar que interfieran entre sí.

El siguiente código implementa los siguientes gestos: arrastrar, tocar, tocar dos veces, hacer clic largo y pellizcar.

static final short NONE = 0; 
static final short DRAG = 1; 
static final short ZOOM = 2; 
static final short TAP = 3; 
static final short DOUBLE_TAP = 4; 
static final short POST_GESTURE = 5; 
short mode = NONE; 
static final float MIN_PINCH_DISTANCE = 30f; 
static final float MIN_DRAG_DISTANCE = 5f; 
static final float DOUBLE_TAP_MAX_DISTANCE = 30f; 
static final long MAX_DOUBLE_TAP_MS = 1000; 
static final long LONG_PRESS_THRESHOLD_MS = 2000; 

public class Vector2d { 
    public float x; 
    public float y; 

    public Vector2d() { 
     x = 0f; 
     y = 0f; 
    } 

    public void set(float newX, float newY) { 
     x = newX; 
     y = newY; 
    } 

    public Vector2d avgVector(Vector2d remote) { 
     Vector2d mid = new Vector2d(); 
     mid.set((remote.x + x)/2, (remote.y + y)/2); 
     return mid; 
    } 

    public float length() { 
     return (float) Math.sqrt(x * x + y * y); 
    } 

    public float distance(Vector2d remote) { 
     float deltaX = remote.x - x; 
     float deltaY = remote.y - y; 
     return (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY); 
    } 
} 

private Vector2d finger1 = new Vector2d(); 
private Vector2d finger2 = new Vector2d(); 
private Vector2d pinchStartDistance = new Vector2d(); 
private Vector2d pinchMidPoint; 
private Vector2d fingerStartPoint = new Vector2d(); 
private long gestureStartTime; 
private Marker selectedMarker; 

@Override 
public boolean onTouch(View v, MotionEvent event) { 
    // Dump touch event to log 
    dumpEvent(event); 

    // Handle touch events here... 
    switch (event.getAction() & MotionEvent.ACTION_MASK) { 
    case MotionEvent.ACTION_DOWN: 
     finger1.set(event.getX(), event.getY()); 
     if (mode == TAP) { 
      if (finger1.distance(fingerStartPoint) < DOUBLE_TAP_MAX_DISTANCE) { 
       mode = DOUBLE_TAP; 
      } else { 
       mode = NONE; 
       gestureStartTime = SystemClock.uptimeMillis(); 
      } 
     } else { 
      gestureStartTime = SystemClock.uptimeMillis(); 
     } 
     fingerStartPoint.set(event.getX(), event.getY()); 
     break; 
    case MotionEvent.ACTION_POINTER_DOWN: 
     finger2.set(event.getX(1), event.getY(1)); 

     pinchStartDistance.set(Math.abs(finger1.x - finger2.x), Math.abs(finger1.y - finger2.y)); 
     Log.d(TAG, String.format("pinch start distance = %f, %f", pinchStartDistance.x, pinchStartDistance.y)); 
     if (pinchStartDistance.length() > MIN_PINCH_DISTANCE) { 
      if (pinchStartDistance.x < MIN_PINCH_DISTANCE) { 
       pinchStartDistance.x = MIN_PINCH_DISTANCE; 
      } 
      if (pinchStartDistance.y < MIN_PINCH_DISTANCE) { 
       pinchStartDistance.y = MIN_PINCH_DISTANCE; 
      } 
      pinchMidPoint = finger1.avgVector(finger2); 
      mode = ZOOM; 
      Log.d(TAG, "mode=ZOOM"); 
     } 
     break; 
    case MotionEvent.ACTION_UP: 
    case MotionEvent.ACTION_POINTER_UP: 
     if (mode == ZOOM) { 
      Vector2d pinchEndDistance = new Vector2d(); 
      pinchEndDistance.set(Math.abs(finger1.x - finger2.x), Math.abs(finger1.y - finger2.y)); 
      if (pinchEndDistance.x < MIN_PINCH_DISTANCE) { 
       pinchEndDistance.x = MIN_PINCH_DISTANCE; 
      } 
      if (pinchEndDistance.y < MIN_PINCH_DISTANCE) { 
       pinchEndDistance.y = MIN_PINCH_DISTANCE; 
      } 
      Log.d(TAG, String.format("pinch end distance = %f, %f", pinchEndDistance.x, pinchEndDistance.y)); 
      zoom(pinchMidPoint, pinchStartDistance.x/pinchEndDistance.x, pinchStartDistance.y/pinchEndDistance.y); 
      // Set mode to "POST_GESTURE" so that when the other finger lifts the handler won't think it was a 
      // tap or something. 
      mode = POST_GESTURE; 
     } else if (mode == NONE) { 
      // The finger wasn't moved enough for it to be considered a "drag", so it is either a tap 
      // or a "long press", depending on how long it was down. 
      if ((SystemClock.uptimeMillis() - gestureStartTime) < LONG_PRESS_THRESHOLD_MS) { 
       Log.d(TAG, "mode=TAP"); 
       mode = TAP; 
       selectedMarker = checkForMarker(finger1); 
       if (selectedMarker != null) { 
        Log.d(TAG, "Selected marker, mode=NONE"); 
        mode = NONE; 
        ((Activity) parent).showDialog(ResultsActivity.DIALOG_MARKER_ID); 
       } 
      } 
      else { 
       Log.d(TAG, "mode=LONG_PRESS"); 
       addMarker(finger1); 
       requestRender(); 
      } 
     } else if (mode == DOUBLE_TAP && (SystemClock.uptimeMillis() - gestureStartTime) < MAX_DOUBLE_TAP_MS) { 
      // The finger was again not moved enough for it to be considered a "drag", so it is 
      // a double-tap. Change the center point and zoom in. 
      Log.d(TAG, "mode=DOUBLE_TAP"); 
      zoom(fingerStartPoint, 0.5f, 0.5f); 
      mode = NONE; 
     } else { 
      mode = NONE; 
      Log.d(TAG, "mode=NONE"); 
     } 
     break; 
    case MotionEvent.ACTION_MOVE: 
     if (mode == NONE || mode == TAP || mode == DOUBLE_TAP) { 
      finger1.set(event.getX(), event.getY()); 
      if (finger1.distance(fingerStartPoint) > MIN_DRAG_DISTANCE) { 
       Log.d(TAG, "mode=DRAG"); 
       mode = DRAG; 
       scroll(fingerStartPoint.x - finger1.x, fingerStartPoint.y - finger1.y); 
      } 
     } 
     else if (mode == DRAG) { 
      scroll(finger1.x - event.getX(), finger1.y - event.getY()); 
      finger1.set(event.getX(), event.getY()); 
     } 
     else if (mode == ZOOM) { 
      for (int i=0; i<event.getPointerCount(); i++) { 
       if (event.getPointerId(i) == 0) { 
        finger1.set(event.getX(i), event.getY(i)); 
       } 
       else if (event.getPointerId(i) == 1) { 
        finger2.set(event.getX(i), event.getY(i)); 
       } 
       else { 
        Log.w(TAG, String.format("Unknown motion event pointer id: %d", event.getPointerId(i))); 
       } 
      } 
     } 
     break; 
    } 

    return true; 
} 

/** Show an event in the LogCat view, for debugging */ 
private void dumpEvent(MotionEvent event) { 
    String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" , 
     "POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" }; 
    StringBuilder sb = new StringBuilder(); 
    int action = event.getAction(); 
    int actionCode = action & MotionEvent.ACTION_MASK; 
    sb.append("event ACTION_").append(names[actionCode]); 
    if (actionCode == MotionEvent.ACTION_POINTER_DOWN 
     || actionCode == MotionEvent.ACTION_POINTER_UP) { 
     sb.append("(pid ").append(
     action >> MotionEvent.ACTION_POINTER_ID_SHIFT); 
     sb.append(")"); 
    } 
    sb.append("["); 
    for (int i = 0; i < event.getPointerCount(); i++) { 
     sb.append("#").append(i); 
     sb.append("(pid ").append(event.getPointerId(i)); 
     sb.append(")=").append((int) event.getX(i)); 
     sb.append(",").append((int) event.getY(i)); 
     if (i + 1 < event.getPointerCount()) 
     sb.append(";"); 
    } 
    sb.append("]"); 
    Log.d(TAG, sb.toString()); 
} 
1

// Este código es para manejar la detección de gestos

final Handler handler = new Handler(); 
private Runnable mLongPressRunnable; 

detector = new GestureDetector(this, new MyGestureDectector()); 
view.setOnTouchListener(new OnTouchListener() { 

     @SuppressLint("ClickableViewAccessibility") 
     @SuppressWarnings("deprecation") 
     @Override 
     public boolean onTouch(View v, MotionEvent event) { 
      detector.onTouchEvent(event); 
      if (event.getAction() == MotionEvent.ACTION_DOWN) { 

       handler.postDelayed(mLongPressRunnable, 1000); 
      } 
      if ((event.getAction() == MotionEvent.ACTION_MOVE) 
        || (event.getAction() == MotionEvent.ACTION_UP)) { 
       handler.removeCallbacks(mLongPressRunnable); 

       } 

      } 

      return true; 
     } 
    }); 
mLongPressRunnable = new Runnable() { 
     public void run() { 
      Toast.makeText(MainActivity.this, "long", Toast.LENGTH_SHORT) 
        .show(); 
     } 
    }; 
class MyGestureDectector implements GestureDetector.OnDoubleTapListener, 
     OnGestureListener { 
     //Implement all the methods 
     } 
+0

PRECAUCIÓN: He encontrado que en al menos un dispositivo, un evento de movimiento se acciona una casi inmediatamente después del evento abajo, incluso si yo no trato de mover mi dedo en absoluto. Por lo tanto, le aconsejo que NO cancele la pulsación larga tan pronto como reciba un evento de movimiento ('removeCallbacks' es efectivamente una cancelación). En su lugar, haga alguna variación en el enfoque del "umbral de movimiento" que se muestra en la respuesta ACEPTABLE. El enfoque 'handler postDelayed/removeCallbacks' de esta respuesta PODRÍA adaptarse para tener dicho umbral de movimiento. – ToolmakerSteve

Cuestiones relacionadas