如何区分移动和点击onTouchEvent()?

在我的应用程序中,我需要处理移动和点击事件。

点击是一个ACTION_DOWN动作,几个ACTION_MOVE动作和一个ACTION_UP动作的序列。 理论上,如果你得到一个ACTION_DOWN事件,然后是一个ACTION_UP事件 – 这意味着用户只是点击你的视图。

但实际上,这个顺序在某些设备上不起作用。 在我的三星Galaxy Gio上,当我点击我的View:ACTION_DOWN,几次ACTION_MOVE,然后ACTION_UP时,我得到这样的序列。 即我用ACTION_MOVE操作代码得到了一些令人意想不到的OnTouchEvent触发器。 我从来没有(或几乎从来没有)序列ACTION_DOWN – > ACTION_UP。

我也不能使用OnClickListener,因为它没有给出点击的位置。 那么如何检测点击事件并将其与移动不同呢?

这是另一个非常简单的解决scheme,不需要担心手指被移动。 如果您只是根据点击的距离移动,那么您如何区分点击和长点击。

你可以把更多的智慧,包括距离移动,但我还没有遇到一个实例,当用户可以移动的距离在200毫秒应该构成一个动作,而不是一个点击。

setOnTouchListener(new OnTouchListener() { private static final int MAX_CLICK_DURATION = 200; private long startClickTime; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { startClickTime = Calendar.getInstance().getTimeInMillis(); break; } case MotionEvent.ACTION_UP: { long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; if(clickDuration < MAX_CLICK_DURATION) { //click event has occurred } } } return true; } }); 

我考虑到了最好的结果:

  1. 主要是在ACTION_DOWNACTION_UP事件之间移动的距离 。 我想指定密度独立像素而不是像素的最大允许距离,以更好地支持不同的屏幕。 例如15 DP
  2. 其次,事件之间的持续时间一秒钟似乎是最好的。 (有些人比较“咕噜”,比如慢慢地“点击”,我还是想承认这一点。)

例:

 /** * Max allowed duration for a "click", in milliseconds. */ private static final int MAX_CLICK_DURATION = 1000; /** * Max allowed distance to move during a "click", in DP. */ private static final int MAX_CLICK_DISTANCE = 15; private long pressStartTime; private float pressedX; private float pressedY; @Override public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: { pressStartTime = System.currentTimeMillis(); pressedX = e.getX(); pressedY = e.getY(); break; } case MotionEvent.ACTION_UP: { long pressDuration = System.currentTimeMillis() - pressStartTime; if (pressDuration < MAX_CLICK_DURATION && distance(pressedX, pressedY, e.getX(), e.getY()) < MAX_CLICK_DISTANCE) { // Click event has occurred } } } } private static float distance(float x1, float y1, float x2, float y2) { float dx = x1 - x2; float dy = y1 - y2; float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy); return pxToDp(distanceInPx); } private static float pxToDp(float px) { return px / getResources().getDisplayMetrics().density; } 

这里的想法和Gem的解决scheme一样 ,有以下不同之处:

  • 这将计算两点之间的实际欧几里德距离 。
  • 这使用dp而不是px。

更新(2015年):也看看加布里埃尔的这个微调版本 。

以Jonik的领导,我build立了一个稍微更好的调整版本,如果你移动你的手指,然后在放手之前返回到现场,

所以这里是我的解决scheme:

 /** * Max allowed duration for a "click", in milliseconds. */ private static final int MAX_CLICK_DURATION = 1000; /** * Max allowed distance to move during a "click", in DP. */ private static final int MAX_CLICK_DISTANCE = 15; private long pressStartTime; private float pressedX; private float pressedY; private boolean stayedWithinClickDistance; @Override public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: { pressStartTime = System.currentTimeMillis(); pressedX = e.getX(); pressedY = e.getY(); stayedWithinClickDistance = true; break; } case MotionEvent.ACTION_MOVE: { if (stayedWithinClickDistance && distance(pressedX, pressedY, e.getX(), e.getY()) > MAX_CLICK_DISTANCE) { stayedWithinClickDistance = false; } break; } case MotionEvent.ACTION_UP: { long pressDuration = System.currentTimeMillis() - pressStartTime; if (pressDuration < MAX_CLICK_DURATION && stayedWithinClickDistance) { // Click event has occurred } } } } private static float distance(float x1, float y1, float x2, float y2) { float dx = x1 - x2; float dy = y1 - y2; float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy); return pxToDp(distanceInPx); } private static float pxToDp(float px) { return px / getResources().getDisplayMetrics().density; } 

使用探测器,它的工作原理,拖动时不会boost

领域:

 private GestureDetector mTapDetector; 

初始化:

 mTapDetector = new GestureDetector(context,new GestureTap()); 

内部类:

 class GestureTap extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDoubleTap(MotionEvent e) { return true; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { // TODO: handle tap here return true; } } 

onTouch:

 @Override public boolean onTouch(View v, MotionEvent event) { mTapDetector.onTouchEvent(event); return true; } 

请享用 :)

为了获得最佳的点击事件识别我们必须考虑两件事情:

  1. ACTION_DOWN和ACTION_UP之间的时差。
  2. 当用户触摸和释放手指时x,y之间的差异。

实际上,我结合了Stimsoni和Neethirajan给出的逻辑

所以这里是我的解决scheme:

  view.setOnTouchListener(new OnTouchListener() { private final int MAX_CLICK_DURATION = 400; private final int MAX_CLICK_DISTANCE = 5; private long startClickTime; private float x1; private float y1; private float x2; private float y2; private float dx; private float dy; @Override public boolean onTouch(View view, MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { startClickTime = Calendar.getInstance().getTimeInMillis(); x1 = event.getX(); y1 = event.getY(); break; } case MotionEvent.ACTION_UP: { long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; x2 = event.getX(); y2 = event.getY(); dx = x2-x1; dy = y2-y1; if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) Log.v("","On Item Clicked:: "); } } return false; } }); 

下面的代码将解决您的问题


     @覆盖
        公共布尔onTouchEvent(MotionEvent事件){
             switch(event.getAction()){
                情况下(MotionEvent.ACTION_DOWN):
                     x1 = event.getX();
                     y1 = event.getY();
                    打破;
                大小写(MotionEvent.ACTION_UP):{
                     x2 = event.getX();
                     y2 = event.getY();
                     dx = x2-x1;
                     dy = y2-y1;

                如果(Math.abs(dx)> Math.abs(dy)) 
                 {
                    如果(dx> 0)移动(1);  //对
                     else if(dx == 0)move(5);  //点击
                    否则移动(2);  //剩下
                 } 
                其他 
                 {
                    如果(dy> 0)移动(3);  // 下
                     else if(dy == 0)move(5);  //点击
                    否则移动(4);  //向上
                 }
                 }
             }
            返回true;
         }

没有ACTION_MOVE发生ACTION_DOWN是非常困难的。 手指在屏幕上的轻微颤动与第一次触摸发生的位置不同,会触发MOVE事件。 另外,我相信手指压力的变化也会触发MOVE事件。 我将在Action_Move方法中使用if语句来确定从原始向下运动发生移动的距离。 如果移动发生在某个设定的半径之外,则会发生MOVE动作。 这可能不是最好的,资源有效的方法来做你的尝试,但它应该工作。

如果您只想对点击做出反应,请使用:

 if (event.getAction() == MotionEvent.ACTION_UP) { } 

添加到上面的答案,如果你想实现onClick和拖动操作,那么我的代码下面可以你们。 从@Stimsoni获得一些帮助:

  // assumed all the variables are declared globally; public boolean onTouch(View view, MotionEvent event) { int MAX_CLICK_DURATION = 400; int MAX_CLICK_DISTANCE = 5; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { long clickDuration1 = Calendar.getInstance().getTimeInMillis() - startClickTime; startClickTime = Calendar.getInstance().getTimeInMillis(); x1 = event.getX(); y1 = event.getY(); break; } case MotionEvent.ACTION_UP: { long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; x2 = event.getX(); y2 = event.getY(); dx = x2-x1; dy = y2-y1; if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) { Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show(); Log.d("clicked", "On Item Clicked:: "); // imageClickAction((ImageView) view,rl); } } case MotionEvent.ACTION_MOVE: long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; x2 = event.getX(); y2 = event.getY(); dx = x2-x1; dy = y2-y1; if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) { //Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show(); // Log.d("clicked", "On Item Clicked:: "); // imageClickAction((ImageView) view,rl); } else { ClipData clipData = ClipData.newPlainText("", ""); View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view); //Toast.makeText(getApplicationContext(), "item dragged", Toast.LENGTH_SHORT).show(); view.startDrag(clipData, shadowBuilder, view, 0); } break; } return false; } 

使用Gil SH答案,我通过实现onSingleTapUp()而不是onSingleTapConfirmed()改进它。 这是更快,不会点击视图如果拖动/移动。

GestureTap:

 public class GestureTap extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { button.performClick(); return true; } } 

像这样使用它:

 final GestureDetector gestureDetector = new GestureDetector(getApplicationContext(), new GestureTap()); button.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { gestureDetector.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: return true; case MotionEvent.ACTION_UP: return true; case MotionEvent.ACTION_MOVE: return true; } return false; } });