Detecting Common Gestures on Android Using GestureDetector

Gestures are those subtle motions to trigger interactions between the touch screen and the user. It lasts for the time between the first touch on the screen to the point when the last finger leaves the surface. We’re all familiar with it as it’s the most common way to communicate with apps on our mobiles, tablets, etc. these days. Some examples are scrolling in an app by swiping vertically/horizontally, pinch to zoom, long press to select and so on.

Android provides us with a class called GestureDetector using which we can detect common gestures like tapping down and up, swiping vertically and horizontally (fling), long and short press, double taps, etc. and attach listeners to them. Let’s see how that’s done.

What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.

Implementing Listeners

We must implement the listeners to start detecting the gesture events. There are 2 ways to do that:

  • Make our Activity class implement GestureDetector.OnDoubleTapListener (for double tap gesture detection) and GestureDetector.OnGestureListener interfaces and implement all the abstract methods.
  • Write a custom class as a nested class of our Activity or some other external class that extends the aforementioned interfaces or extends the GestureDetector.SimpleOnGestureListener class. The beauty of SimpleOnGestureListener nested class is that it implements all the methods from those 2 interfaces but does nothing. So if we want to deal with and process only a few (subset) gestures then we can take this approach.

I’ll demonstrate how to implement the 2 interfaces using the second approach:

class CustomGestureDetector implements GestureDetector.OnGestureListener,
                                        GestureDetector.OnDoubleTapListener {

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        mGestureText.setText("onSingleTapConfirmed");
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        mGestureText.setText("onDoubleTap");
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        mGestureText.setText("onDoubleTapEvent");
        return true;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        mGestureText.setText("onDown");
        return true;
    }

    @Override
    public void onShowPress(MotionEvent e) {
        mGestureText.setText("onShowPress");
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        mGestureText.setText("onSingleTapUp");
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        mGestureText.setText("onScroll");
        return true;
    }

    @Override
    public void onLongPress(MotionEvent e) {
        mGestureText.setText("onLongPress");
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        mGestureText.setText("onFling");
        return true;
    }
}

Although must of these methods are self-explanatory, in a state of confusion refer to the documentation of OnDoubleTapListener and OnGestureListener.

Creating the GestureDetector

Now, we have to create a GestureDetector object and attach the listener objects to it which will intercept the gesture events. We’ll do that in the onCreate() method of our Activity class:

private TextView mGestureText;
private GestureDetector mGestureDetector;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_gesture);

    mGestureText = (TextView) findViewById(R.id.gestureStatus);

    // Create an object of our Custom Gesture Detector Class
    CustomGestureDetector customGestureDetector = new CustomGestureDetector();
    // Create a GestureDetector
    mGestureDetector = new GestureDetector(this, customGestureDetector);
    // Attach listeners that'll be called for double-tap and related gestures
    mGestureDetector.setOnDoubleTapListener(customGestureDetector);
}

Pretty straightforward piece of code. One thing to notice is that, since we want to detect double-taps and related gestures, we call the setOnDoubleTapListener() on our GestureDetector object.

Implementing onTouchEvent()

Our gesture detectors won’t fire yet. This is because we arn’t intercepting the touch events and re-routing them to our gesture detectors. In order to do that we’ll have to override our Activity’s onTouchEvent() method and do the re-routing there like this:

@Override
public boolean onTouchEvent(MotionEvent event) {
    mGestureDetector.onTouchEvent(event);

    return super.onTouchEvent(event);
}

Wow, that was really easy! Now you should just go and start testing the code out by running the app on your physical device and making various gestures on the screen.

Note: If you want to capture the touch events on a particular view rather than the entire Activity, then we’ll need to attach a View.OnTouchListener object to the View object using setOnTouchListener from whose onTouch() method the re-routing to the gesture detectors will need to be done:

view.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, final MotionEvent event) {
        mGestureDetector.onTouchEvent(event);
        return true;
    }
});

There’s another way to do the same thing which is to subclass a View class and override it’s onTouchEvent() method to do the delegation but that’s a little complicated and messy.

Bonus: Detecting Swipe/Fling Direction

Detecting the fling directions like up to down, down to up, left to right and right to left can be a requirement which is fairly easy to implement inside the onFling() method:

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    mGestureText.setText("onFling " + e1.getX() + " - " + e2.getX());

    if (e1.getX() < e2.getX()) {
        Log.d(TAG, "Left to Right swipe performed");
    }

    if (e1.getX() > e2.getX()) {
        Log.d(TAG, "Right to Left swipe performed");
    }

    if (e1.getY() < e2.getY()) {
        Log.d(TAG, "Up to Down swipe performed");
    }

    if (e1.getY() > e2.getY()) {
        Log.d(TAG, "Down to Up swipe performed");
    }

    return true;
}

e1 MotionEvent object contains data regarding the first down motion event that started the fling, i.e., the first down interaction with the touch screen by the finger (pointer), whereas, the e2 object contains data regarding the move motion event (end of gesture) that triggered onFling(). getX/Y() is used to get the X and Y co-ordinates.

Conclusion

The GestureDetector class provided to us makes it really easy to detect most of the common gestures from basic physical contact with the screen like tapping to swiping and scrolling. Once detected we can build our app functionality on their invocation. Later we’ll see how to implement custom gestures like pinch to zoom.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Author: Rishabh

Rishabh is a full stack web and mobile developer from India. Follow me on Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *