Logo Icon Logo
A Crowd-sourced Cookbook on Writing Great Android® Apps
GitHub logo Twitter logo OReilly Book Cover Art

Detecting Gestures in Android

Author: Pratik Rupwal -- Published? true -- FormatLanguage: W

Problem:

You want to traverse through different screens using simple gestures like flip/scroll the page.

Solution:

In Android we can detect simple gestures using the 'GestureDetector' class. This class can be used to detect simple gestures like tap, scroll, swipe or flip, etc.

Discussion:

The application has 4 views and each view has different color. It has and 2 modes, SCROLL mode and FLIP mode. The application starts in FLIP mode. In

this mode when you perform the swipe/fling gesture in left right, up and down direction, the view changes back and forth. When a long-press is detected, the application changes to SCROLL mode, in this mode you scroll the displayed view. While in this mode, you can double-tap on the screen to bring back the screen to its original position. Again when a long-press is detected the application changes to FLIP mode.

This recipe focuses on gesture detection hence the animation applied is not discussed briefly. Refer to 'android.view.animation.*' for animation.

This will give you an introduction to simple gesture detection in Android.

import java.util.ArrayList;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.OvershootInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.TextView;
import android.widget.ViewFlipper;

/**
 * GestureDetector class detects gestures using the supplied MotionEvent class.
 * We use this class along with the onTouchEvent, inside this method we call the
 * GestureDetector.onTouchEvent. GestureDetector identify the gestures or events
 * that occurred and report back to us using GestureDetector.OnGestureListener
 * callback interface. We create an instance of the GestureDetector class by
 * passing Context and GestureDetector.OnGestureListener listener. Double-tap
 * event is not present in theGestureDetector.onGestureListener callback
 * interface, this event is reported using another callback interface
 * GestureDetector.onDoubleTapListener. To use this callback interface we have
 * to register for these events using GestureDetector.setOnDoubleTapListener.
 * The MotionEvent class contains all the values correspond to a movement and
 * touch event. This class holds values such as X and Y position at which the
 * event occurred, timestamp at which the event occurred, mouse pointer index,
 * etc.
 */
public class FlipperActivity extends Activity implements GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener{
        
        final private int SWIPE_MIN_DISTANCE = 100;
        final private int SWIPE_MIN_VELOCITY = 100;
        
        private ViewFlipper flipper = null;
        private ArrayList<TextView> views = null;
        private GestureDetector gesturedetector = null;
        private Vibrator vibrator = null;
        int colors[] = { Color.rgb(255,128,128), 
            Color.rgb(128,255,128), 
            Color.rgb(128,128,255),
            Color.rgb(128,128,128) };
        
        private Animation animleftin = null;
        private Animation animleftout = null;
        
        private Animation animrightin = null;
        private Animation animrightout = null;
        
        private Animation animupin = null;
        private Animation animupout = null;
        
        private Animation animdownin = null;
        private Animation animdownout = null;
        
        private boolean isDragMode = false;
        private int currentview = 0;
      
/** Initializes the first screen and animation to be applied to the screen after detecting the gesture */
  
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        flipper = new ViewFlipper(this);
        gesturedetector = new GestureDetector(this, this);
        vibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);
        gesturedetector.setOnDoubleTapListener(this);
        
        flipper.setInAnimation(animleftin);
        flipper.setOutAnimation(animleftout);
        flipper.setFlipInterval(3000);
        flipper.setAnimateFirstView(true);

        prepareAnimations();
        prepareViews();
        addViews();
        setViewText();
        
        setContentView(flipper);
    }

        private void prepareAnimations() {
                animleftin = new TranslateAnimation(
                        Animation.RELATIVE_TO_PARENT,  +1.0f, Animation.RELATIVE_TO_PARENT,  0.0f,
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,   0.0f);
                        
        animleftout = new TranslateAnimation(
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  -1.0f,
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  0.0f);
        
        animrightin = new TranslateAnimation(
                        Animation.RELATIVE_TO_PARENT,  -1.0f, Animation.RELATIVE_TO_PARENT,  0.0f,
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,   0.0f);
                        
        animrightout = new TranslateAnimation(
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  +1.0f,
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  0.0f);
        
        animupin = new TranslateAnimation(
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  0.0f,
                        Animation.RELATIVE_TO_PARENT,  +1.0f, Animation.RELATIVE_TO_PARENT,   0.0f);
                        
        animupout = new TranslateAnimation(
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  0.0f,
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  -1.0f);
        
        animdownin = new TranslateAnimation(
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  0.0f,
                        Animation.RELATIVE_TO_PARENT,  -1.0f, Animation.RELATIVE_TO_PARENT,   0.0f);
                        
        animdownout = new TranslateAnimation(
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  0.0f,
                        Animation.RELATIVE_TO_PARENT,  0.0f, Animation.RELATIVE_TO_PARENT,  +1.0f);
        
        animleftin.setDuration(1000);
        animleftin.setInterpolator(new OvershootInterpolator());
        animleftout.setDuration(1000);
        animleftout.setInterpolator(new OvershootInterpolator());
        
        animrightin.setDuration(1000);
        animrightin.setInterpolator(new OvershootInterpolator());
        animrightout.setDuration(1000);
        animrightout.setInterpolator(new OvershootInterpolator());
        
        animupin.setDuration(1000);
        animupin.setInterpolator(new OvershootInterpolator());
        animupout.setDuration(1000);
        animupout.setInterpolator(new OvershootInterpolator());
        
        animdownin.setDuration(1000);
        animdownin.setInterpolator(new OvershootInterpolator());
        animdownout.setDuration(1000);
        animdownout.setInterpolator(new OvershootInterpolator());
        }
        
        private void prepareViews(){
                TextView view = null;
                
                views = new ArrayList<TextView>();
                
                for(int color: colors)
                {
                        view = new TextView(this);
                        
                        view.setBackgroundColor(color);
                        view.setTextColor(Color.BLACK);
                        view.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
                        
                        views.add(view);
                }
        }
        
        private void addViews(){
                for(int index=0; index<views.size(); ++index)
                {
                        flipper.addView(views.get(index),index,
                                        new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
                }
        }
        
        private void setViewText(){
                String text = getString(isDragMode ? R.string.app_info_drag : R.string.app_info_flip);
                for(int index=0; index<views.size(); ++index)
                {
                        views.get(index).setText(text);
                }
        }

	/**Gets invoked when a screen touch is detected*/

        @Override
        public boolean onTouchEvent(MotionEvent event) {
                return gesturedetector.onTouchEvent(event);
        }
	
	/**The onDown method is called when the user first touch the screen, the MotionEvent parameter represents the event that corresponds
 to the touch event. */
	
        @Override
        public boolean onDown(MotionEvent e) {
                return false;
        }

	/**The onFling method is called whenever the user swipes the screen in any direction, i.e. the user touches the screen and immediately
 moves the finger in any direction.*/

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX,float velocityY) {
                if(isDragMode)
                        return false;
                
                final float ev1x = event1.getX();
                final float ev1y = event1.getY();
                final float ev2x = event2.getX();
                final float ev2y = event2.getY();
                final float xdiff = Math.abs(ev1x - ev2x);
                final float ydiff = Math.abs(ev1y - ev2y);
                final float xvelocity = Math.abs(velocityX);
                final float yvelocity = Math.abs(velocityY);
                
                if(xvelocity > this.SWIPE_MIN_VELOCITY && xdiff > this.SWIPE_MIN_DISTANCE)
                {
                        if(ev1x > ev2x) //Swipe Left
                        {
                                --currentview;
                                
                                if(currentview < 0)
                                {
                                        currentview = views.size() - 1;
                                }
                                
                                flipper.setInAnimation(animleftin);
                                flipper.setOutAnimation(animleftout);
                        }
                        else //Swipe Right
                        {
                                ++currentview;
                                
                                if(currentview >= views.size())
                                {
                                        currentview = 0;
                                }
                                
                                flipper.setInAnimation(animrightin);
                                flipper.setOutAnimation(animrightout);
                        }
                        
                        flipper.scrollTo(0,0);
                        flipper.setDisplayedChild(currentview);
                }
                else if(yvelocity > this.SWIPE_MIN_VELOCITY && ydiff > this.SWIPE_MIN_DISTANCE)
                {
                        if(ev1y > ev2y) //Swipe Up
                        {
                                --currentview;
                                
                                if(currentview < 0)
                                {
                                        currentview = views.size() - 1;
                                }
                                
                                flipper.setInAnimation(animupin);
                                flipper.setOutAnimation(animupout);
                        }
                        else //Swipe Down
                        {
                                ++currentview;
                                
                                if(currentview >= views.size())
                                {
                                        currentview = 0;
                                }
                                flipper.setInAnimation(animdownin);
                                flipper.setOutAnimation(animdownout);
                        }
                        
                        flipper.scrollTo(0,0);
                        flipper.setDisplayedChild(currentview);
                }
                                
                return false;
        }

	/** The onLongPress method is called when user touches the screen and holds it for a period of time. The MotionEvent parameter represents
 the event that corresponds to the touch event. */

        @Override
        public void onLongPress(MotionEvent e) {
                vibrator.vibrate(200);
                flipper.scrollTo(0,0);
                
                isDragMode = !isDragMode;
                
                setViewText();
        }

	/**The onScroll method is called when the user touches the screen and moves to another location on the screen.*/

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,float distanceY) {
                if(isDragMode)
                        flipper.scrollBy((int)distanceX, (int)distanceY);
                
                return false;
        }

	/**The onShowPress method is called when the user touches the phone and not moved yet. This event is mostly used for giving visual feedback
 to the user to show their action.*/

        @Override
        public void onShowPress(MotionEvent e) {
        }

	/**The onSingleTapUp method is called when a tap occurred, i.e. user taps the screen.*/
	
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
                return false;
        }

	/** The onDoubleTap method is called when there is a double-tap event occurred. The only parameter MotionEvent corresponds to the double-tap
 event that occurred. */

        @Override
        public boolean onDoubleTap(MotionEvent e) {
                flipper.scrollTo(0,0);
                
                return false;
        }

	/** The onDoubleTapEvent is called for all events that occurred within the double-tap, i.e. down, move and up events.*/

        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
                return false;
        }

	/** The onSingleTapConfirmed method is called when there is a single tap occurred and confirmed, but this is not same as the single-tap
 event in the GestureDetector.onGestureListener. This is called when the GestureDetector detects and confirms that this tap does not

lead to a double-tap. */

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
                return false;
        }
}

When the mode of the application changes it is notified to the user with a vibration. To use the vibrator set the following permission in 'androidmanifest.xml' file of your application.

<uses-permission android:name="android.permission.VIBRATE"></uses-permission>

The Application uses some strings which are declared under 'res->values->string.xml'

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <string name="app_info_drag">GestureDetector sample.\n\nCurrent Mode: SCROLL\n\nDrag the view using finger.\nLong press to change the mode to
FLIP.\nDouble tap to reposition the view to normal.</string>
    <string name="app_name">Gesture Detector Sample</string>
    <string name="app_info_flip">GestureDetector sample.\n\nCurrent Mode: FLIP\n\nSwipe left, right, up, down to change the views\nLong press to
change to mode to SCROLL</string>
</resources>

See Also:

Check 'GestureOverlayView' class for handling complex gestures in android.

No records found.