Advertisement

Android SDK: Detecting Gestures

by

The Android SDK supports a variety of gestures you can detect, allowing you to tailor the ways in which users interact with your applications. In this tutorial, we will look at the framework that allows developers to add gesture detection to a simple application, working through the process of supporting the fling gesture and outlining how to support other gestures. Once you've completed the development for a basic fling function, you'll be able to add additional gestures to the application yourself.


1. Create a New Application

Step 1

To demonstrate gesture detection in Android, we will build a simple application to display messages to the user. The user will be able to cycle forward and backward through the messages using the fling gesture, which involves swiping a finger or stylus across the screen from left to right or right to left. In reality, with this type of application, the message content would typically be read from a data source, but to simplify the task we will simply store the messages in an array. Create a new Android project and let Eclipse create a main Activity class for it with a main layout.

Open the layout of the application and use the following basic content. It is fine to leave the default parent layout added by Eclipse in place and simply adjust the contained TextView to match the one below.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  tools:context=".MainActivity" >

  <TextView
    android:id="@+id/the_message"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

</RelativeLayout>

If necessary, modify the name of the main class listed as tools:context attribute for the layout to suit your own main Activity class. The layout includes a TextView with an id attribute so that we can refer to it in the implementation of the Activity class.

Step 2

This gives us a basic layout with a text view for displaying the messages. We can now implement the Activity class. Start by opening the class and inspecting its contents.

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

public class MainActivity extends Activity {

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

Add the following import statements to the list at the top of the class implementation.

import android.support.v4.view.GestureDetectorCompat;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.TextView;

2. Prepare the User Interface

Step 1

Let's prepare the application for displaying messages. Add the following instance variables before the onCreate method.

//message text view
private TextView messageView;
//message array
private String[] messages;
//total messages
private int numMessages = 10;
//current message -start at zero
private int currMessage = 0;

As mentioned earlier, you wouldn't normally hard code data into an application, but we will do so in this case so that we can focus on detecting gestures, the main subject of this tutorial. We have a variable that keeps a reference to the text view we added to the layout, an array for the messages, a variable to record the total number of messages, and a counter to keep track of which message is currently being displayed.

Step 2

Inside the onCreate method immediately after the line setting the content view, retrieve a reference to the text view using its id and store a reference in messageView as shown below.

messageView = (TextView)findViewById(R.id.the_message);

In the next code snippet, we instantiate the messages array.

messages = new String[numMessages];

For simplicity, let's make each message some dummy text containing a number so we can easily see which message is being displayed.

for(int i=0; i<numMessages; i++){
  messages[i]="Message "+i+":\n\n"+
      "Here is message "+i+":\n\n"+
      "Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
      + "sed do eiusmod tempor incididunt ut labore et dolore magna "
      + "aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
      + "ullamco laboris nisi ut aliquip ex ea commodo consequat. "
      + "Duis aute irure dolor in reprehenderit in voluptate velit "
      + "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
      + "occaecat cupidatat non proident, sunt in culpa qui officia "
      + "deserunt mollit anim id est laborum.";
}

After the loop, set tell the text view to display the first message of the messages array.

messageView.setText(messages[currMessage]);

3. Detect Gestures

Step 1

You have a few options when it comes to preparing your activity classes to detect gestures. You can set up gesture detection on individual views in your application by setting an onTouchListener on them, with the onTouch method implemented inside your touch listener. To allow the activity as a whole to detect gestures, you can make the class implement the GestureDetector interface(s) and supply methods for each gesture. Possible gestures include onDoubleTap, onDown, onFling, onLongPress, onScroll, onShowPress, and onSingleTapUp. See Detecting Common Gestures in the Developer Guide for possible options.

If you only want to support a subset of gestures, you can use the more convenient option we will demonstrate in this tutorial, that is, adding a class that extends SimpleOnGestureListener. By doing so, the class handles any gestures you are not supplying explicit instructions for in your overridden methods.

Add an inner class declaration inside your activity class, after the onCreate method, extending SimpleOnGestureListener.

public class GestureListener extends GestureDetector.SimpleOnGestureListener {
//class content
}

Inside this class, we will define what should happen when a particular gesture is detected, the fling gesture in our example.

Step 2

Although we are primarily interested in the fling gesture, when using SimpleOnGestureListener you need to implement the onDown method at a minimum, since the system may otherwise ignore remaining gesture events. Add the method inside your new inner class as shown below.

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

Returning true tells the operating system that your code is interested in the remaining gesture events. After onDown, add a stub implementation for the onFling method. We will discuss its implementation a bit later in this tutorial.

@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
    float velocityX, float velocityY) {
  //determine what happens on fling events
}

Step 3

Before we get to work on the detail of what should happen when the fling gesture is detected, we need to complete gesture detection for the class. Add an instance variable for a gesture detector at the top of the activity class, before the onCreate method.

//gesture detector
private GestureDetectorCompat gDetect;

Inside the onCreate method and after the existing code, instantiate the gesture detector by passing an instance of the new class we created.

gDetect = new GestureDetectorCompat(this, new GestureListener());

After the onCreate method, we need to override the onTouchEvent method to ensure that the gesture listener is used when a touch events is detected.

@Override
public boolean onTouchEvent(MotionEvent event){
  this.gDetect.onTouchEvent(event);
  return super.onTouchEvent(event);
}

The method calls the onTouchEvent method for the gesture detector, which will fire the appropriate gesture methods inside the inner class. Next, we will focus on the detail of what will happen when the user flings the messages in our application, but take a moment to look at the structure of your class for future reference. This is an approach you can use for supporting a set of gestures in any activity, that is, adding an inner class to extend SimpleOnGestureListener, overriding onTouchEvent to call the onTouchEvent method for a gesture detector you instantiated using an object of your SimpleOnGestureListener class.


4. Fling Messages

Step 1

Finally, we can focus on what happens when the fling gesture is detected. In your inner class, start by adding a couple of variables we need to offset the calculations for optimum reliability (before the onDown and onFling methods).

private float flingMin = 100;
private float velocityMin = 100;

In your own applications, you may find the need to tweak these values to suit your interaction models. Inside onFling, declare two boolean variables to keep track of the direction the messages are moving in.

//user will move forward through messages on fling up or left
boolean forward = false;
//user will move backward through messages on fling down or right
boolean backward = false;

We will interpret flings to the left or up as a desire to move forward and flings down or to the right as a desire to move backwards through the messages. This is a common UX paradigm in messaging and email applications.

Step 2

Note that the onFling method receives four parameters, representing the down and move motions that triggered the event and the velocity of the gesture in x and y directions. We'll use these parameters to determine the direction of the fling. We then calculate the x and y differences for the two motion events.

//calculate the change in X position within the fling gesture
float horizontalDiff = event2.getX() - event1.getX();
//calculate the change in Y position within the fling gesture
float verticalDiff = event2.getY() - event1.getY();

Now calculate the absolute values for these measures as well as the velocity values.

float absHDiff = Math.abs(horizontalDiff);
float absVDiff = Math.abs(verticalDiff);
float absVelocityX = Math.abs(velocityX);
float absVelocityY = Math.abs(velocityY);

Step 3

We only want to respond to the fling gesture if it exceeds the offset values we defined. Start with the case where the horizontal difference is greater than the vertical difference.

if(absHDiff>absVDiff && absHDiff>flingMin && absVelocityX>velocityMin){
//move forward or backward
}

Inside the if block, set the boolean variables according to whether the user gesture indicates a forward or backward motion.

if(horizontalDiff>0) backward=true;
else forward=true;

Still inside the onFling method, after the outer if statement, add an else if for cases where the horizontal difference is not greater than the vertical one.

else if(absVDiff>flingMin && absVelocityY>velocityMin){
  if(verticalDiff>0) backward=true;
  else forward=true;
}

We check once more that the values exceed the minimum thresholds we defined earlier so that we only respond to gestures that can reasonably be interpreted as fling gestures. Again, we set the forward or backward variable to true.

Step 4

Still inside onFling, update the displayed message according to the user gesture.

//user is cycling forward through messages
if(forward){
  //check current message is not at end of array
  //increment or set back to start
  if(currMessage<numMessages-1) currMessage++;
  else currMessage = 0;
  //set the message text display
  messageView.setText(messages[currMessage]);
}
//user is cycling backwards through messages
else if(backward){
  //check that current message is not at start of array
  //decrement or set to last message
  if(currMessage>0) currMessage--;
  else currMessage = numMessages-1;
  //set the message text display
  messageView.setText(messages[currMessage]);
}

If the gesture indicates that the user is moving forward through the messages, we first check if they are already at the last message, incrementing the variable or setting it back to the first message and we then set the message text to display. If the user is moving backwards through the messages, we check if the first message is currently being displayed, decrementing the variable or setting it to the last message.

Finish the onFling method implementation by returning true.

return true;

You can now test your application by running it on an actual device or in the emulator. Flinging to the left or up will display the next message, while moving to the previous message when flinging to the right or down.

Flinging Messages

The application after flinging to the left or down three times from launch.

Conclusion

In this tutorial, we explored adding gesture detection to an Android application, creating the structures you need in your activity classes, and working through a simple example using the fling gesture. Take a look at the source code of this tutorial if you are unsure about any of the instructions. If you want to explore gestures in more detail, extend the code you created to support other gestures in your application. As you can see, adding support for detecting gestures gives you a fine level of control over the user experience within your applications, although which gestures you need to handle will naturally depend on the functionality of the application.

Advertisement