如何区分移动和点击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; } });
我考虑到了最好的结果:
- 主要是在
ACTION_DOWN
和ACTION_UP
事件之间移动的距离 。 我想指定密度独立像素而不是像素的最大允许距离,以更好地支持不同的屏幕。 例如15 DP 。 - 其次,事件之间的持续时间 。 一秒钟似乎是最好的。 (有些人比较“咕噜”,比如慢慢地“点击”,我还是想承认这一点。)
例:
/** * 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; }
请享用 :)
为了获得最佳的点击事件识别我们必须考虑两件事情:
- ACTION_DOWN和ACTION_UP之间的时差。
- 当用户触摸和释放手指时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; } });
- 如何使用匿名函数removeEventListener作为addEventListener?
- Android重写onBackPressed()
- 如何在不触发Text Watcher的情况下更改EditText文本?
- 如何在Swing中停止重复的keyPressed()/ keyReleased()事件
- inputtypes=“文件”的jQuery更改方法
- 编写内联事件处理程序是不好的做法
- 如何使用Firebug(或类似工具)debuggingJavaScript / jQuery事件绑定
- e.target和e.currentTarget之间的区别
- jQuery等价于JavaScript的addEventListener方法