Android:检测用户是否触摸并拖出button区域?

在Android中,我们如何检测用户是否触摸了button并拖出了此button的区域?

检查MotionEvent.MOVE_OUTSIDE:检查MotionEvent.MOVE:

private Rect rect; // Variable rect to hold the bounds of the view public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN){ // Construct a rect of the view's bounds rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); } if(event.getAction() == MotionEvent.ACTION_MOVE){ if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){ // User moved outside bounds } } return false; } 

注意:如果你想瞄准Android 4.0,一个新的可能性的整个世界打开: http : //developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER

Entreco发布的答案需要在我的情况下稍微调整。 我不得不取代:

 if(!rect.contains((int)event.getX(), (int)event.getY())) 

对于

 if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) 

因为event.getX()event.getY()只适用于ImageView本身,而不是整个屏幕。

我在我的OnTouch中添加了一些日志logging,发现MotionEvent.ACTION_CANCEL正在被击中。 这对我来说足够了…

前2个答案是好的,除非视图在scrollview中:当你移动手指进行滚动时,它仍然被注册为一个触摸事件,但不是一个MotionEvent.ACTION_MOVE事件。 所以为了改善答案(只有当你的视图在滚动元素中时才需要):

 private Rect rect; // Variable rect to hold the bounds of the view public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN){ // Construct a rect of the view's bounds rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); } else if(rect != null && !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){ // User moved outside bounds } return false; } 

我在Android 4.3和Android 4.4上testing了这个

我没有注意到莫里茨的答案和前两名之间有什么不同,但这也适用于他的回答:

 private Rect rect; // Variable rect to hold the bounds of the view public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN){ // Construct a rect of the view's bounds rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); } else if (rect != null){ v.getHitRect(rect); if(rect.contains( Math.round(v.getX() + event.getX()), Math.round(v.getY() + event.getY()))) { // inside } else { // outside } } return false; } 

虽然@FrostRocket的答案是正确的,但您应该使用view.getX()和Y来解释翻译更改:

  view.getHitRect(viewRect); if(viewRect.contains( Math.round(view.getX() + event.getX()), Math.round(view.getY() + event.getY()))) { // inside } else { // outside } 

这里是一个View.OnTouchListener ,你可以用它来查看View.OnTouchListener MotionEvent.ACTION_UP在用户把手指放在视图之外的时候发送的:

 private OnTouchListener mOnTouchListener = new View.OnTouchListener() { private Rect rect; @Override public boolean onTouch(View v, MotionEvent event) { if (v == null) return true; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); return true; case MotionEvent.ACTION_UP: if (rect != null && !rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { // The motion event was outside of the view, handle this as a non-click event return true; } // The view was clicked. // TODO: do stuff return true; default: return true; } } }; 
 view.setClickable(true); view.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (!v.isPressed()) { Log.e("onTouch", "Moved outside view!"); } return false; } }); 

view.isPressed使用view.pointInView并包含一些触摸斜坡。 如果你不想要view.pointInView ,只需从内部view.pointInView (这是公开的,但隐藏的,所以它不是官方的API的一部分,可以随时消失)的逻辑复制。

 view.setClickable(true); view.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { v.setTag(true); } else { boolean pointInView = event.getX() >= 0 && event.getY() >= 0 && event.getX() < (getRight() - getLeft()) && event.getY() < (getBottom() - getTop()); boolean eventInView = ((boolean) v.getTag()) && pointInView; Log.e("onTouch", String.format("Dragging currently in view? %b", pointInView)); Log.e("onTouch", String.format("Dragging always in view? %b", eventInView)); v.setTag(eventInView); } return false; } }); 

我有和OP一样的问题,我想知道什么时候(1)特定的View被触及,以及(2)当向下触摸在View上被释放时,或者(3)当向下触摸移动到View边界。 我在这个线程中汇集了各种答案来创buildView.OnTouchListener (名为SimpleTouchListener )的简单扩展,以便其他人不必摆弄MotionEvent对象。 这个类的来源可以在这里或者在这个答案的底部find。

要使用此类,只需将其设置为View.setOnTouchListener(View.OnTouchListener)方法的单个参数View.setOnTouchListener(View.OnTouchListener)

 myView.setOnTouchListener(new SimpleTouchListener() { @Override public void onDownTouchAction() { // do something when the View is touched down } @Override public void onUpTouchAction() { // do something when the down touch is released on the View } @Override public void onCancelTouchAction() { // do something when the down touch is canceled // (eg because the down touch moved outside the bounds of the View } }); 

以下是您可以添加到项目中的类的来源:

 public abstract class SimpleTouchListener implements View.OnTouchListener { /** * Flag determining whether the down touch has stayed with the bounds of the view. */ private boolean touchStayedWithinViewBounds; @Override public boolean onTouch(View view, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: touchStayedWithinViewBounds = true; onDownTouchAction(); return true; case MotionEvent.ACTION_UP: if (touchStayedWithinViewBounds) { onUpTouchAction(); } return true; case MotionEvent.ACTION_MOVE: if (touchStayedWithinViewBounds && !isMotionEventInsideView(view, event)) { onCancelTouchAction(); touchStayedWithinViewBounds = false; } return true; case MotionEvent.ACTION_CANCEL: onCancelTouchAction(); return true; default: return false; } } /** * Method which is called when the {@link View} is touched down. */ public abstract void onDownTouchAction(); /** * Method which is called when the down touch is released on the {@link View}. */ public abstract void onUpTouchAction(); /** * Method which is called when the down touch is canceled, * eg because the down touch moved outside the bounds of the {@link View}. */ public abstract void onCancelTouchAction(); /** * Determines whether the provided {@link MotionEvent} represents a touch event * that occurred within the bounds of the provided {@link View}. * * @param view the {@link View} to which the {@link MotionEvent} has been dispatched. * @param event the {@link MotionEvent} of interest. * @return true iff the provided {@link MotionEvent} represents a touch event * that occurred within the bounds of the provided {@link View}. */ private boolean isMotionEventInsideView(View view, MotionEvent event) { Rect viewRect = new Rect( view.getLeft(), view.getTop(), view.getRight(), view.getBottom() ); return viewRect.contains( view.getLeft() + (int) event.getX(), view.getTop() + (int) event.getY() ); } }