Остановить OnLongClickListener от запуска при перетаскивании - PullRequest
9 голосов
/ 08 июня 2011

У меня есть пользовательский вид с растровыми изображениями, который пользователь может перетаскивать.

Я хочу сделать так, чтобы, когда они долго нажимали на одну из них, я мог вызвать контекстное меню с такими параметрами, как положение сброса и т. Д.

В пользовательском представлении я добавляю свой OnLongClickListener:

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

И переопределите onTouchEvent, чтобы оно выглядело примерно так:

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

Функция handleDrag находит, какой объект был нажат, и обрабатывает обновление его положения.

Моя проблема в том, что когда я начинаю перетаскивать изображение, также запускается OnLongClickListener. Я не уверен, что лучший способ обойти это.

Я пытался добавить порог к handleDrag, чтобы он возвращал значение false, если пользователь прикасается, но не пытается перетащить, но мне все еще трудно заставить корректно сработать обработчик.

Может кто-нибудь предложить способ пропустить OnLongClickListener при перетаскивании?

Ответы [ 3 ]

7 голосов
/ 08 июня 2011

Я думаю, что я решил эту проблему путем настройки моего порогового подхода.

Сначала я изменил свой onTouchEvent, чтобы он выглядел так:

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

Теперь они оба запускаются, поэтому я изменил свой OnLongClickListener на следующее:

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

(mMultiTouchController - это класс, содержащий весь мой код обнаружения жестов). Ключ здесь находится внутри этого класса, я добавил bool 'has_moved'. Когда я начинаю перетаскивать, я вычисляю дельту:

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

Теперь, когда срабатывает onLongClick, я знаю, следует ли действовать или нет.

Последний кусок должен был установить:

setHapticFeedbackEnabled(false);

в моем представлении, чтобы пользователь не вибрировал каждый раз, когда запускается longClick, но никаких действий не предпринимается. Я планирую сделать вибрацию вручную в качестве следующего шага.

Пока что это нормально, надеюсь, это поможет любому, кто сталкивался с подобной ситуацией, как эта.

4 голосов
/ 08 июня 2011

Я бы прекратил использовать onLongClickListener и просто реализовал свой собственный, что довольно легко сделать. Тогда у вас есть контроль, который вам нужен, чтобы они не мешали друг другу.

Следующий код реализует следующие жесты: перетаскивание, касание, двойное касание, длительное нажатие и сжатие.

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 голос
/ 21 августа 2014

// Этот код предназначен для обнаружения жестов

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