阻止 OnLongClickListener 在拖动时触发

发布于 2024-11-14 12:04:25 字数 746 浏览 10 评论 0原文

我有一个带有位图的自定义视图,用户可以拖动它。

我想做到这一点,这样当他们长按其中一个时,我可以弹出一个上下文菜单,其中包含重置位置等选项。

在自定义视图中,我添加我的 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 的方法吗?

I have a custom View with bitmaps on it that the user can drag about.

I want to make it so when they long click one of them I can pop up a context menu with options such as reset position etc.

In the custom View I add my OnLongClickListener:

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

And override onTouchEvent to look something like this:

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

The handleDrag function finds what object is been pressed, and handles updating it's position.

My problem is that when I start to drag an image the OnLongClickListener fires also. I'm not sure the best way around this.

I've tried adding a threshold to handleDrag to return false if user touches down but doesn't attempt to drag, but I'm finding it still difficult to get the correct handler fired.

Can anyone suggest a way to skip the OnLongClickListener while dragging?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

流年里的时光 2024-11-21 12:04:25

我想我已经通过调整阈值方法解决了这个问题。

首先,我将 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 是包含我所有手势检测代码的类)。
这里的关键是在这个类中,我添加了布尔值“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 触发时收到振动但不采取任何操作。我计划下一步手动进行振动。

到目前为止似乎还可以,希望对遇到类似情况的人有所帮助。

I think I have this solved through tweaking my threshold approach.

First, I changed my onTouchEvent to look like this:

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

They now both fire, so I then changed my OnLongClickListener to the following:

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

(mMultiTouchController is the class containing all my gesture detection code).
The key here is within this class, I added the bool 'has_moved'. When I go to start a drag I then compute the delta:

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

Now when the onLongClick fires I know whether to take action or not.

The final piece was to set:

setHapticFeedbackEnabled(false);

in my View so that the user doesn't get a vibrate every time the longClick fires but no action is taken. I plan to do the vibration manually as a next step.

This seems to be ok so far, hope that helps anyone who has come across a similar situation as this one.

帅冕 2024-11-21 12:04:25

我将停止使用 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());
}

I would stop using the onLongClickListener and just implement your own, which is pretty easy to do. Then you have the control you need to keep them from interfering with each other.

The following code implements the following gestures: drag, tap, double tap, long click, and pinch.

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());
}
心奴独伤 2024-11-21 12:04:25

//此代码用于处理手势检测

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
        }

//This code is to handle the gestures detection

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
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文