Gesture Detection (swipe detection)

Simple swipe detection. I know this example code has been done elsewhere, but I wanted it here for my own purposes as well.  The example code uses a ListActivity and places the listener where it needs to go in order to detect swipes on the list.

UPDATE 2012 March 27: Android 4.0 introduced a bug in how gesture events propagate through the View tree. If a child view contains an onLongPressListener, the system passes NULL into onFling() for e1, which should have been the onDown() event. In order to workaround this issue, it is necessary to save off the onDown() event “just in case” it is needed later for an onFling() event.

public class MyActivity extends ListActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);  
        mGestureDetector = new GestureDetector(new MyGestureDetector());
        mGestureListener = new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent aEvent) {
                if (mGestureDetector.onTouchEvent(aEvent))
                    return true;
                else
                    return false;
                }
            };
        getListView().setOnTouchListener(mGestureListener);
    }

    GestureDetector mGestureDetector = null;
    View.OnTouchListener mGestureListener = null;

    class MyGestureDetector extends SimpleOnGestureListener {
        private static final int SWIPE_MIN_DISTANCE = 150;
        private static final int SWIPE_MAX_OFF_PATH = 100;
        private static final int SWIPE_THRESHOLD_VELOCITY = 100;
        private MotionEvent mLastOnDownEvent = null;

        @Override
        public boolean onDown(MotionEvent e) {
            //Android 4.0 bug means e1 in onFling may be NULL due to onLongPress eating it.
            mLastOnDownEvent = e;
            return super.onDown(e);
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            if (e1==null)
                e1 = mLastOnDownEvent;
            if (e1==null || e2==null)
                return false;
            float dX = e2.getX()-e1.getX();
            float dY = e1.getY()-e2.getY();
            if (Math.abs(dY)<SWIPE_MAX_OFF_PATH &&
                Math.abs(velocityX)>=SWIPE_THRESHOLD_VELOCITY &&
                Math.abs(dX)>=SWIPE_MIN_DISTANCE ) {
                if (dX>0) {
                    Toast.makeText(getApplicationContext(), "Right Swipe", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(getApplicationContext(), "Left Swipe", Toast.LENGTH_SHORT).show();
                }
                return true;
            } else if (Math.abs(dX)<SWIPE_MAX_OFF_PATH &&
                Math.abs(velocityY)>=SWIPE_THRESHOLD_VELOCITY &&
                Math.abs(dY)>=SWIPE_MIN_DISTANCE ) {
                if (dY>0) {
                    Toast.makeText(getApplicationContext(), "Up Swipe", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(getApplicationContext(), "Down Swipe", Toast.LENGTH_SHORT).show();
                }
                return true;
            }
            return false;
        }
    };
}

I personally use this code for a ListView that contains several ImageViews and TextViews and the swipe is detected no matter which view starts the event. The listener is being attached to the ListView itself and not any of the child views that get created by the adapter associated with the list. As long as none of the child views handle the swipe, the event will bubble up to the ListView and get handled by our listener.

2 thoughts on “Gesture Detection (swipe detection)

Leave a Reply

Your email address will not be published. Required fields are marked *