Detecting Sliding Gestures in Android
April 1, 2014Detecting 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.
Looking for more like this?
Sign up for our monthly newsletter to receive helpful articles, case studies, and stories from our team.
Lessons Learned from our Associate Developer
September 13, 2023One of our Associate Software Developers, Rohit, reflects on his time at MichiganLabs working on a short-term project, what he learned about real-world development, and the software consultancy business model.
Read moreThe 5 Minute Accessibility Strategy
May 18, 2023We discuss how you can make a plan in just 5 minutes to provide accessibility in your mobile app.
Read moreUX Writing Tips
February 3, 2023Kai shares a few tips he's collected on how to write for user interfaces.
Read more