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.
![Build what matters: Prioritize value over feature count](https://gratis-creeper.transforms.svdcdn.com/staging/blog/value-delivery-thumbnail.jpg?w=1080&h=1080&auto=compress%2Cformat&fit=crop&dm=1722534218&s=7e3eb61ab73a30709d5d40f227777492)
Build what matters: Prioritize value over feature count
August 1, 2024Focusing on value delivery—rather than just feature count—combines your business goals with your users’ needs to achieve real software ROI.
Read more![3 tips for navigating tech anxiety as an executive](https://gratis-creeper.transforms.svdcdn.com/staging/blog/2231952_Tech-Anxiety-Blog-1080x1080_031324.jpg?w=1080&h=1080&auto=compress%2Cformat&fit=crop&dm=1711474447&s=0892959f5ec8b54ff26789a7e628edc2)
3 tips for navigating tech anxiety as an executive
March 13, 2024C-suite leaders feel the pressure to increase the tempo of their digital transformations, but feel anxiety from cybersecurity, artificial intelligence, and challenging economic, global, and political conditions. Discover how to work through this.
Read more![What the smartest executive leaders are doing right now](https://gratis-creeper.transforms.svdcdn.com/staging/blog/Smartest-execs_1080x1080_040424.jpg?w=2250&h=2250&auto=compress%2Cformat&fit=crop&dm=1721919838&s=b986884e81efdeed51ef17bec2bbd746)
What the smartest executive leaders are doing right now
July 25, 2024We are consistently seeing three things right now in terms of how executives are tackling the technological challenges they are facing.
Read more