Android: Detect if user touches and drags out of button region?

前端 未结 9 1983
孤城傲影
孤城傲影 2020-12-04 16:33

In Android, how can we detect if a user touches on button and drags out of region of this button?

相关标签:
9条回答
  • 2020-12-04 16:50

    While the answer from @FrostRocket is correct you should use view.getX() and Y to account for translations changes as well:

     view.getHitRect(viewRect);
     if(viewRect.contains(
             Math.round(view.getX() + event.getX()),
             Math.round(view.getY() + event.getY()))) {
       // inside
     } else {
       // outside
     }
    
    0 讨论(0)
  • 2020-12-04 16:51

    Reuseable Kotlin Solution

    I started with two custom extension functions:

    val MotionEvent.up get() = action == MotionEvent.ACTION_UP
    
    fun MotionEvent.isIn(view: View): Boolean {
        val rect = Rect(view.left, view.top, view.right, view.bottom)
        return rect.contains((view.left + x).toInt(), (view.top + y).toInt())
    }
    

    Then listen to touches on the view. This will only fire if ACTION_DOWN was originally on the view. When you release your finger, it will check if it was still on the view.

    myView.setOnTouchListener { view, motionEvent ->
        if (motionEvent.up && !motionEvent.isIn(view)) {
            // Talk your action here
        }
        false
    }
    
    0 讨论(0)
  • 2020-12-04 16:59

    I had this same problem as the OP whereby I wanted to know when (1) a particular View was touched down as well as either (2a) when the down touch was released on the View or (2b) when the down touch moved outside the bounds of the View. I brought together the various answers in this thread to create a simple extension of View.OnTouchListener (named SimpleTouchListener) so that others don't have to fiddle around with the MotionEvent object. The source for this class can be found here or at the bottom of this answer.

    To use this class, simply set it as the parameter of the View.setOnTouchListener(View.OnTouchListener) method follows:

    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
            // (e.g. because the down touch moved outside the bounds of the View
        }
    });
    

    Here is the source of the class which you are welcome to add to your project:

    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,
         * e.g. 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()
            );
        }
    }
    
    0 讨论(0)
  • 2020-12-04 17:01

    Check the MotionEvent.MOVE_OUTSIDE: Check the 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;
    }
    

    NOTE: If you want to target Android 4.0, a whole world of new possibilities opens: http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER

    0 讨论(0)
  • 2020-12-04 17:03

    The answer posted by Entreco needed some slight tweaking in my case. I had to substitute:

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

    for

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

    because event.getX() and event.getY() only apply to the ImageView itself, not the entire screen.

    0 讨论(0)
  • 2020-12-04 17:03

    Here is a View.OnTouchListener that you can use to see if MotionEvent.ACTION_UP was sent while the user had his/her finger outside of the view:

    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;
            }
        }
    };
    
    0 讨论(0)
提交回复
热议问题