9

我有一个自定义视图与位图,用户可以拖动。停止OnLongClickListener从发射而拖动

我想使它所以当他们长单击其中之一,我可以弹出的选项,如复位位置等

自定义视图中的上下文菜单添加我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也会触发。我不确定解决这个问题的最佳方法。

我已经尝试添加一个阈值来处理拖动,如果用户触摸但不尝试拖动但返回false,但我发现它仍然很难得到正确的处理程序解雇。

任何人都可以建议一种方法来拖动时跳过OnLongClickListener?

回答

7

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

首先,我改变了我的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火灾,但没有采取行动。我计划下一步手动进行振动。

到目前为止,这似乎还行,希望能帮助任何遇到类似情况的人。

3

我会停止使用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

//此代码是处理手势检测

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

注意:我发现至少一个设备上,则向下事件之后,几乎立刻引发了移动事件,即使我尽量不要动我的手指。因此,我建议您在收到移动事件后不要取消长按(“removeCallbacks”实际上是取消)。相反,请在接受答案中显示的“移动阈值”方法上进行一些修改。此答案的'处理程序postDelayed/removeCallbacks'方法可以适应有这样的移动阈值。 – ToolmakerSteve 2016-10-20 16:53:05