Search code examples
javaandroidandroid-studiotouchswipe

Detect swipe gestures and touch events on a view


Android Studio, Java

Is there a way to detect touch down, touch release, and swipe gestures, on a single view? I have a button and I need to be able to listen to all of these on it.

I'm looking for a solution where the listeners/detectors end up calling these methods so I easily can work with them:

public void buttonPressedDown() {

}

public void buttonReleased() {

}

public void swipeUp() {

}

public void swipeRight() {

}

public void swipeDown() {

}

public void swipeLeft() {

}

This is easy to accomplish in xcode when developing ios apps, but I can't seem to find a way to do it for android.

Extra info: This is what I've tried:

    timerButton.setOnTouchListener(new View.OnTouchListener() { //Detecting a press down
        @Override
        public boolean onTouch(View v, MotionEvent event) {

            timerDown();

            return false;
        }
    });

    timerButton.setOnTouchListener(new OnSwipeTouchListener(timerActivity.this) {//Detecting swipes

        public void onSwipeTop() {

            showStats();

        }

        public void onSwipeBottom() {

            showType();

        }

        public void onSwipeRight() {

            showMenu();

        }

        public void onSwipeLeft() {

            showTimes();

        }

    });


    .
    .
    .


    public void timerButtonPressed(View view) {//Detecting press releases
        timerUp();
    }

This seemed like what I was looking for, except it didn't work. When setting two onTouchListeners on the same view, only one of the listeners were being called. Either only the swipe detections worked, or only the press detections, when I tested it.

Help!?

Edit: I've tried this:

OnSwipeTouchListener.java

public class OnSwipeTouchListener implements View.OnTouchListener {

    private final GestureDetector gestureDetector;

    public OnSwipeTouchListener(Context context) {
        gestureDetector = new GestureDetector(context, new GestureListener());
    }

    public void onSwipeLeft() {
    }

    public void onSwipeRight() {
    }

    public boolean onTouch(View v, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }

    private final class GestureListener extends GestureDetector.SimpleOnGestureListener {

        private static final int SWIPE_DISTANCE_THRESHOLD = 100;
        private static final int SWIPE_VELOCITY_THRESHOLD = 100;

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            float distanceX = e2.getX() - e1.getX();
            float distanceY = e2.getY() - e1.getY();
            if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                if (distanceX > 0)
                    onSwipeRight();
                else
                    onSwipeLeft();
                return true;
            }
            return false;
        }
    }
}

And in my activity:

timerButton.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {

            OnSwipeTouchListener onSwipeTouchListener = new OnSwipeTouchListener(timerActivity.this);
            boolean result = onSwipeTouchListener.onTouch(v, event);
            // check if swipe was detected:
            if (result) return true;

            // Otherwise, detect other types of motion event
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // touch down

                    timerDown();

                    break;
                case MotionEvent.ACTION_UP:
                    // touch released

                    timerUp();

                    break;
            }

            return false;
        }
    });

But how would I detect the swipes from here? Also when I run the application it doesn't even detect touches anymore.

I also tried this in my activity:

timerButton.setOnTouchListener(new OnSwipeTouchListener(timerActivity.this) {

        public void onSwipeLeft() {
            Log.i("LEFT", "LEFT");
        }

        public void onSwipeRight() {
            Log.i("RIGHT", "RIGHT");
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // touch down

                    timerDown();

                    break;
                case MotionEvent.ACTION_UP:
                    // touch released

                    timerUp();

                    break;
            }

            return false;
        }
    });

Now it detects touches but won't detect swipes. If I remove the @override onTouch method above, but still have the onSwipe methods, then the swipes are detected. How is this so complicated? Why won't it detect both swipes and touches, and how do I solve it?


Solution

  • I found a solution.

    In OnSwipeTouchListener.java:

    public class OnSwipeTouchListener implements View.OnTouchListener {
    
    private final GestureDetector gestureDetector;
    
    public OnSwipeTouchListener(Context context) {
        gestureDetector = new GestureDetector(context, new GestureListener());
    }
    
    public void onSwipeLeft() {
    }
    
    public void onSwipeRight() {
    }
    
    public void onSwipeBottom() {
    }
    
    public void onSwipeTop() {
    }
    
    public void onDownTouch() {
    
    }
    
    public boolean onTouch(View v, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }
    
    private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
    
        private static final int SWIPE_DISTANCE_THRESHOLD = 100;
        private static final int SWIPE_VELOCITY_THRESHOLD = 100;
    
        @Override
        public boolean onDown(MotionEvent e) {
            onDownTouch();
            return false;
        }
    
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            boolean result = false;
            try {
                float diffY = e2.getY() - e1.getY();
                float diffX = e2.getX() - e1.getX();
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            onSwipeRight();
                        } else {
                            onSwipeLeft();
                        }
                        result = true;
                    }
                }
                else if (Math.abs(diffY) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        onSwipeBottom();
                    } else {
                        onSwipeTop();
                    }
                    result = true;
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return result;
        }
    }
    

    }

    And in activity:

    To detect button release press: (on click)

    public void timerButtonPressed(View view) {
    
    }
    

    To detect swipes and down press:

    timerButton.setOnTouchListener(new OnSwipeTouchListener(timerActivity.this) {
    
            public void onSwipeLeft() {
    
            }
    
            public void onSwipeRight() {
    
            }
    
            public void onSwipeBottom() {
    
            }
    
            public void onSwipeTop() {
    
            }
    
            public void onDownTouch() {
    
            }
        });