Detecting Sliding Gestures in Android

April 1, 2014

Detecting and recognizing gestures in an Android app can make intuitive shortcuts for your users to perform various tasks. Google’s GMail app uses sliding gestures such as its swipe to delete or archive inbox emails and their YouTube app uses sliding gestures to shrink and dismiss videos that are currently playing.

Detecting a sliding gesture across a View in Android seems like a simple task but it can be a little tricky. Let’s walk through how to make a simple sliding gesture detector, and get you started on the road to detecting gestures in your apps.

Let’s start with a simple activity with a single view in its layout:

package com.example.SlidingGestureExample;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;

public class MyActivity extends Activity {

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.my_activity);

 View myView = (View) findViewById(R.id.myView);
 }
}

Ultimately, what we would like to see is a nice listener that tells us when certain gestures are detected on this view, like this:

package com.example.SlidingGestureExample;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;

public class MyActivity extends Activity {

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.my_activity);

 View myView = (View) findViewById(R.id.myView);
 myView.setOnTouchListener(new OnSlidingTouchListener(this) {
 @Override
 public boolean onSlideLeft() {
 // do something
 return true;
 }

 @Override
 public boolean onSlideRight() {
 // do something
 return true;
 }

 @Override
 public boolean onSlideUp() {
 // do something
 return true;
 }

 @Override
 public boolean onSlideDown() {
 // do something
 return true;
 }
 });
 }
}

Let’s make our custom touch listener class:

package com.example.SlidingGestureExample;

import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class OnSlidingTouchListener implements OnTouchListener {

 @Override
 public boolean onTouch(View v, MotionEvent event) {
 return false;
 }
}

In order to detect a somewhat complex gesture like a slide in a particular direction, we will need to make use of the Android GestureDetector class. Add a GestureDetector as a field in our custom touch listener class, create a constructor that takes in a Context as an arugment, and in onTouch(), pass the MotionEvent to the GestureDetector to handle.

public class OnSlidingTouchListener implements OnTouchListener {

 private final GestureDetector gestureDetector;

 public OnSlidingTouchListener(Context context){
 gestureDetector = new GestureDetector(context, /* we need a GestureListener here */);
 }

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

GestureDetector’s constructor must be provided with a GestureDetector.OnGestureListener. We’ll provide it with a private inner class implementation of SimpleOnGestureListener.

public class OnSlidingTouchListener implements OnTouchListener {

 private final GestureDetector gestureDetector;

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

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

 private final class GestureListener extends SimpleOnGestureListener { }
}

GestureListener, our implementation of SimpleOnGestureListener, will be used to indicate when a sliding gesture has been received. SimpleOnGestureListener has no methods to override regarding sliding gestures, but it does have one called onScoll(). We will use the typical scrolling gesture to detect a sliding gesture.

private final class GestureListener extends SimpleOnGestureListener {

 private final String TAG = GestureListener.class.getSimpleName();

 private static final int SLIDE_THRESHOLD = 100;

 @Override
 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
 try {
 float deltaY = e2.getY() - e1.getY();
 float deltaX = e2.getX() - e1.getX();

 if (Math.abs(deltaX) > Math.abs(deltaY)) {
 if (Math.abs(deltaX) > SLIDE_THRESHOLD) {
 if (deltaX > 0) {
 // the user made a sliding right gesture
 return onSlideRight();
 } else {
 // the user made a sliding left gesture
 return onSlideLeft();
 }
 }
 } else {
 if (Math.abs(deltaY) > SLIDE_THRESHOLD) {
 if (deltaY > 0) {
 // the user made a sliding down gesture
 return onSlideDown();
 } else {
 // the user made a sliding up gesture
 return onSlideUp();
 }
 }
 }
 } catch (Exception exception) {
 Log.e(TAG, exception.getMessage());
 }

 return false;
 }
 }

What’s happening in our implementation of onScroll()? We first calculate the distance between the relative (to our View) pixel coordinates of the starting MotionEvent e1 and the coordinates of the ending MotionEvent e2. If the horizontal change, deltaX, is greater than the vertical change, deltaY, then we can assume the user made a horizontal sliding motion, and if the vertical change was greater than the horizontal change, then a vertical sliding motion was made. If the greater change is larger than a certain threshold, in this case we use 100 pixels, then, depending on whether the change was positive or negative, we have detected a right/up or left/down slide gesture, respectively.

Now let’s handle the callbacks for each of these gestures:

public class OnSlidingTouchListener implements OnTouchListener {

 private final GestureDetector gestureDetector;

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

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

 private final class GestureListener extends SimpleOnGestureListener {

 private final String TAG = GestureListener.class.getSimpleName();

 private static final int SLIDE_THRESHOLD = 100;

 @Override
 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
 try {
 float deltaY = e2.getY() - e1.getY();
 float deltaX = e2.getX() - e1.getX();

 if (Math.abs(deltaX) > Math.abs(deltaY)) {
 if (Math.abs(deltaX) > SLIDE_THRESHOLD) {
 if (deltaX > 0) {
 // the user made a sliding right gesture
 return onSlideRight();
 } else {
 // the user made a sliding left gesture
 return onSlideLeft();
 }
 }
 } else {
 if (Math.abs(deltaY) > SLIDE_THRESHOLD) {
 if (deltaY > 0) {
 // the user made a sliding down gesture
 return onSlideDown();
 } else {
 // the user made a sliding up gesture
 return onSlideUp();
 }
 }
 }
 } catch (Exception exception) {
 Log.e(TAG, exception.getMessage());
 }

 return false;
 }
 }

 public boolean onSlideRight() {
 return false;
 }

 public boolean onSlideLeft() {
 return false;
 }

 public boolean onSlideUp() {
 return false;
 }

 public boolean onSlideDown() {
 return false;
 }
}

A very important piece in here is the implementation of onDown(). If this isn’t implemented to return true, then none of your gestures will appear to be recognized.

Each of these callbacks, onSlideRight(), onSlideLeft(), onSlideUp(), and onSlideDown(), are all meant to be overridden in our touch listener. Let’s go back to our activity and our View’s touch listener and add these methods:

package com.example.SlidingGestureExample;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;

public class MyActivity extends Activity {

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.my_activity);

 View myView = (View) findViewById(R.id.myView);
 myView.setOnTouchListener(new OnSlidingTouchListener(this) {
 @Override
 public boolean onSlideLeft() {
 // do something
 return true;
 }

 @Override
 public boolean onSlideRight() {
 // do something
 return true;
 }

 @Override
 public boolean onSlideUp() {
 // do something
 return true;
 }

 @Override
 public boolean onSlideDown() {
 // do something
 return true;
 }
 });
 }
}

Now myView can recognize and react to sliding gestures in each of the four directions, and handle each accordingly. In each of these, we return true to signal that the gesture was recognized and consumed, and we return false if we didn’t do anything with it.

The code from this post can be found in this Github Gist.

Ready to get started?

Call us at (616) 594-0269 or send us a note below.
Visit our office @ 452 Ada Drive SE Suite 300 Ada, Michigan 49301
Send us an e-mail @ info@michiganlabs.com