How to detect swipe direction between left/right and up/down
Asked Answered
E

13

87

My Question: How do I detect when a user moves their finger up/down vs left/right (and how do I know which direction of those groups their finger moved)?

My Situation: I want to change the brightness of my app when they move their finger up and down (up = brighter, down = darker), and I want to switch between activities and/or views based on their left/right swipe.

Electrometallurgy answered 26/10, 2012 at 22:54 Comment(1)
Try this: OnSwipeTouchListener.java: #4139788Pilkington
B
196

I wrote a simple class for this: it's well documented so I wont explain it here

public class OnSwipeListener extends GestureDetector.SimpleOnGestureListener {

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

        // Grab two events located on the plane at e1=(x1, y1) and e2=(x2, y2)
        // Let e1 be the initial event
        // e2 can be located at 4 different positions, consider the following diagram
        // (Assume that lines are separated by 90 degrees.)
        //
        //
        //         \ A  /
        //          \  /
        //       D   e1   B
        //          /  \
        //         / C  \
        //
        // So if (x2,y2) falls in region:
        //  A => it's an UP swipe
        //  B => it's a RIGHT swipe
        //  C => it's a DOWN swipe
        //  D => it's a LEFT swipe
        //

        float x1 = e1.getX();
        float y1 = e1.getY();

        float x2 = e2.getX();
        float y2 = e2.getY();

        Direction direction = getDirection(x1,y1,x2,y2);
        return onSwipe(direction);
    }

    /** Override this method. The Direction enum will tell you how the user swiped. */
    public boolean onSwipe(Direction direction){
        return false;
    }

    /**
     * Given two points in the plane p1=(x1, x2) and p2=(y1, y1), this method
     * returns the direction that an arrow pointing from p1 to p2 would have.
     * @param x1 the x position of the first point
     * @param y1 the y position of the first point
     * @param x2 the x position of the second point
     * @param y2 the y position of the second point
     * @return the direction
     */
    public Direction getDirection(float x1, float y1, float x2, float y2){
        double angle = getAngle(x1, y1, x2, y2);
        return Direction.fromAngle(angle);
    }

    /**
     *
     * Finds the angle between two points in the plane (x1,y1) and (x2, y2)
     * The angle is measured with 0/360 being the X-axis to the right, angles
     * increase counter clockwise.
     *
     * @param x1 the x position of the first point
     * @param y1 the y position of the first point
     * @param x2 the x position of the second point
     * @param y2 the y position of the second point
     * @return the angle between two points
     */
    public double getAngle(float x1, float y1, float x2, float y2) {

        double rad = Math.atan2(y1-y2,x2-x1) + Math.PI;
        return (rad*180/Math.PI + 180)%360;
    }


    public enum Direction{
        up,
        down,
        left,
        right;

        /**
         * Returns a direction given an angle.
         * Directions are defined as follows:
         *
         * Up: [45, 135]
         * Right: [0,45] and [315, 360]
         * Down: [225, 315]
         * Left: [135, 225]
         *
         * @param angle an angle from 0 to 360 - e
         * @return the direction of an angle
         */
        public static Direction fromAngle(double angle){
            if(inRange(angle, 45, 135)){
                return Direction.up;
            }
            else if(inRange(angle, 0,45) || inRange(angle, 315, 360)){
                return Direction.right;
            }
            else if(inRange(angle, 225, 315)){
                return Direction.down;
            }
            else{
               return Direction.left;
           }

        }

       /**
         * @param angle an angle
         * @param init the initial bound
         * @param end the final bound
         * @return returns true if the given angle is in the interval [init, end).
         */
        private static boolean inRange(double angle, float init, float end){
            return (angle >= init) && (angle < end);
        }
    }
 }

To use simply extend the OnSwipeListener and override the onSwipe method

Birck answered 15/10, 2014 at 16:35 Comment(13)
Far the best implementation of the 4-way swipe detection I tried so far (in terms of detection accuracy). Thanks.Allstar
How I implement this from Fragment?Ideomotor
Sure @AhmedI.Elsayed: Step 1: copy the class to a file named OnSwipeListener.java. Step 2: Create a new class (e.g. MySwipeListener) that extends OnSwipeListener. Step 3: Override the onSwipe(Direction) method.Birck
Nah, I understand that of course. I meant, I can't use it because I can't extend activity & that at the same time because of limitations of Java. How can I use that on an xml while I can't link that xml to a java file at the same time?Thesis
Can you please just suggest anything about using it inside a class that extends an activity?Thesis
Look for my answer for usage exampleLuthanen
Thanks @fernandohur, but where or how can I set an action for swipeUp, swipeDown, etc.Wristlet
I used an interface, a constructor, and the @farhan-patel response to get mine to work...Poppied
To force the override of onSwipe, mark it and the class as abstract.Gibe
This seems to be an elegant code snippet. I did integrate it into one of my projects, in which it applied to a webview class. However while sliding up/down works like a charm, sliding left/right is a pain in the neck - it only occasionally worked. I don't know it's because the way I did wasn't right, or my cell phone is not sensitivity (then how come it works fine for up/down) or something else? I'll try another phone and come back to update.Sow
how to use this class against a view in android?Pederast
Only missing a min-swipe-distance and min-velocityTrinl
You don't need to use atan2 and all that math to determine quadrant. Just check if Math.abs(x2-x1) or Math.abs(y2-y1) is bigger, and then check the sign of the difference. An example is here: https://mcmap.net/q/74031/-android-how-to-handle-right-to-left-swipe-gesturesDisfrock
D
64

You simply have to extend SimpleOnGestureListener class,

Declare this in your class,

private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_MAX_OFF_PATH = 250;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;

As an example for horizontal swipe you can see the below code,

 class MyGestureDetector extends SimpleOnGestureListener {
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        try {
            if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH){
                return false;
            }
            // right to left swipe
            if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                onLeftSwipe();
            } 
            // left to right swipe
            else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
                    && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                onRightSwipe();
            }
        } catch (Exception e) {

        }
        return false;
      }
   }

You can do this similarly for vertical swipe purpose.

Dilatant answered 26/10, 2012 at 23:0 Comment(9)
What do I used for those constants you indicated in your answer?Electrometallurgy
That represents the distance of swipe i.e the minimum distanc the user should swipe for invoking the purpose,same for others as the name suggests.Dilatant
How can I use that inside the class that extends Activity?Absher
@Absher This code which I wrote was used inside an activity ,MyGestureDetector is an inner class and the variables are the attributes for that Activity.Dilatant
Why do you return false not true, haven't you handled the gesture as you want? Also why is the body inside a try/catch statement?Douala
How can I check if such swipe was done with one finger only?Honeybunch
You may find this sample project helpful github.com/Asad-noor/SwipeTouchSampleAppPew
Do NOT use getX(), use getRawX() ONLY, if you want to get stable values.Extremist
how do you extend multiple classes.Genitals
H
37

Fernandour answer is perfect, I am writing this answer about how to use it with Activity and Fragment as many people are looking for it.

public class MyActivity extends Activity implements View.OnTouchListener{

     private RelativeLayout someLayout;
     //take any layout on which you want your gesture listener;

     @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    gestureDetector=new GestureDetector(this,new OnSwipeListener(){

        @Override
        public boolean onSwipe(Direction direction) {
            if (direction==Direction.up){
               //do your stuff
                Log.d(TAG, "onSwipe: up");

            }

            if (direction==Direction.down){
               //do your stuff
                Log.d(TAG, "onSwipe: down");
            }
            return true;
        }


    });
    someLayout.setOnTouchListener(this);
}

    @Override
    public boolean onTouch(View v, MotionEvent event) {
      Log.d(TAG, "onTouch: ");
      gestureDetector.onTouchEvent(event);
      return true;
  }


}
Horrify answered 4/1, 2017 at 11:58 Comment(0)
L
23

A full Usage Example for fernandohur answer above:

If you want to apply OnSwipeListener to one of your views, so:
Wherever this view is - set a touch listener for that view, like this:

myview.setOnTouchListener(this);

Now in your Activity's OnCreate or in your Custom view constructor do this:

// Global
private GestureDetectorCompat detector; 

// In OnCreate or custome view constructor (which extends one of Android views)
detector = new GestureDetectorCompat(context, onSwipeListener);

Override in the same class the onTouch event, like this:

@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
    return detector.onTouchEvent(motionEvent);
}

And also have in the same class this listener object:

OnSwipeListener onSwipeListener = new OnSwipeListener() {
    @Override
    public boolean onSwipe(Direction direction) {
        // Possible implementation 
        if(direction == Direction.left|| direction == Direction.right) {
            // Do something COOL like animation or whatever you want
            // Refer to your view if needed using a global reference
            return true;
        }
        else if(direction == Direction.up|| direction == Direction.down) {
            // Do something COOL like animation or whatever you want
            // Refer to your view if needed using a global reference
            return true;
        }
        return super.onSwipe(direction);
    }
};
Luthanen answered 30/1, 2017 at 15:49 Comment(0)
W
5

The best answer that i test it several times and its worked for me

float firstX_point, firstY_point;
@Override
public boolean onTouchEvent(MotionEvent event) {

    int action = event.getAction();

    switch (action) {

        case MotionEvent.ACTION_DOWN:
            firstX_point = event.getRawX();
            firstY_point = event.getRawY();
            break;

        case MotionEvent.ACTION_UP:

            float finalX = event.getRawX();
            float finalY = event.getRawY();

            int distanceX = (int) (finalX - firstX_point);
            int distanceY = (int) (finalY - firstY_point);

            if (Math.abs(distanceX) > Math.abs(distanceY)) {
                if ((firstX_point < finalX)) {
                    Log.d("Test", "Left to Right swipe performed");
                } else {
                    Log.d("Test", "Right to Left swipe performed");
                }
            }else{
                if ((firstY_point < finalY)) {
                    Log.d("Test", "Up to Down swipe performed");
                } else {
                    Log.d("Test", "Down to Up swipe performed");
                }
            }


            break;
    }

    return true;
}
Weightless answered 9/2, 2021 at 15:30 Comment(0)
R
3

that is how I did it, easiest way

float initialX, initialY;

@Override
public boolean onTouchEvent(MotionEvent event) {

    int action = event.getActionMasked();

    switch (action) {

        case MotionEvent.ACTION_DOWN:
            initialX = event.getX();
            initialY = event.getY();


            // Log.d(TAG, "Action was DOWN");
            break;

        case MotionEvent.ACTION_MOVE:

            //Log.d(TAG, "Action was MOVE");
            break;

        case MotionEvent.ACTION_UP:
            float finalX = event.getX();
            float finalY = event.getY();


            //Log.d(TAG, "Action was UP");

            if (initialX < finalX) {
                // Log.d(TAG, "Left to Right swipe performed");
            }

            if (initialX > finalX) {
                // Log.d(TAG, "Right to Left swipe performed");
            }

            if (initialY < finalY) {
                // Log.d(TAG, "Up to Down swipe performed");
            }

            if (initialY > finalY) {
                // Log.d(TAG, "Down to Up swipe performed");
            }

            break;

        case MotionEvent.ACTION_CANCEL:
            //Log.d(TAG,"Action was CANCEL");
            break;

        case MotionEvent.ACTION_OUTSIDE:
            // Log.d(TAG, "Movement occurred outside bounds of current screen element");
            break;
    }

    return super.onTouchEvent(event);
}
Raviv answered 17/8, 2019 at 10:40 Comment(0)
P
2

You could override SimpleGestureListener and calculate the diff between start end current coordinates:

private class GestureListener extends SimpleOnGestureListener {

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

        if (e2.getY() > e1.getY()) {
            // direction up
        }else {
            // direction down
        }

        if (e2.getX() > e1.getX()) {
            // direction right
        }else {
            // direction left
        }

        return true;
    }
}
Payne answered 15/1, 2018 at 13:0 Comment(1)
This answer is not correct. Let e=(x,y) be the x,y coordinates of event e. Let e1 = (0,0) and e2 = (200, 1) In this simple example (the user swiped 200 pixels on the x axis and 1 pixel on the y axis). Your code would have interpreted this as a Y axis swipe, which won't make much sense for the user.Birck
P
1

I solved this way:

viewPager.setOnTouchListener(new View.OnTouchListener() {
        float prevX = -1;

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (prevX != -1) {
                if (event.getX() > prevX) {
                    if (viewPager.getCurrentItem() == 0) {
                         // Left to Right swipe
                    }
                    //Log.d("DEBUG", MotionEvent.ACTION_MOVE + ":" + event.getAction() + ":" + event.getActionMasked() + ":Left Swipe" + ":" + prevX + ":" + event.getX() + ":" + viewPager.getCurrentItem());
                } else if (prevX > event.getX()) {
                       // Right to left swipe
                    //Log.d("DEBUG", MotionEvent.ACTION_MOVE + ":" + event.getAction() + ":" + event.getActionMasked() + ":Right Swipe" + ":" + prevX + ":" + event.getX() + ":" + viewPager.getCurrentItem());
                }
            }
            if (event.getAction() == MotionEvent.ACTION_MOVE) {
                prevX = event.getX();
            } else {
                prevX = -1;
            }
            return false;
        }
    });
Pouter answered 24/5, 2016 at 8:1 Comment(0)
D
0

I have an open source gesture library on bitbucket that does this. Within this library is a 'HGFling' class. This demonstrates how to detect the direction of a fling. You can download the library from: https://bitbucket.org/warwick/hacergestov3. It's open source.

Doeskin answered 11/5, 2015 at 9:45 Comment(0)
L
0

The answers available are far too complex for such a simple problem. I suggest using Kotlin in another approach to it (code is as3, but you can get the idea):

var touchDistance:Number = Point.distance(_moveTouchPoint, _startTouchPoint);
if (touchDistance >= SWIPE_MIN_DISTANCE)
{
    var xDiff:Number = _moveTouchPoint.x - _startTouchPoint.x;
    var yDiff:Number = _moveTouchPoint.y - _startTouchPoint.y;
    
    var yGreater:Boolean = Math.abs(yDiff) >= Math.abs(xDiff);
    if (yGreater)
    {
        // direction is up or down
        changePlayerDirectionTo(yDiff < 0 ? DIRECTION_UP : DIRECTION_DOWN);
    }
    else
    {
        // direction is left or right
        changePlayerDirectionTo(xDiff < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT)
    }
}

In each case either x or y will be greater in absolute value, which can resolve to certain direction set. From then on you can rely on the coordinate sign to detect which direction exactly.

Laaspere answered 28/4, 2017 at 8:21 Comment(0)
H
0

Adding Kotlin implementation for @Fernandour answer. For Java look at @farhan patel answer I am adding this because I was having difficulty, hope it will save someone#s time.

class ClientFragment : Fragment(), View.OnTouchListener {

    private lateinit var gestureDetector: GestureDetector

    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        Log.d(TAG, "onTouch: ");
        gestureDetector.onTouchEvent(event);
        return true
    }


override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

 ...



 gestureDetector = GestureDetector(activity, object : OnSwipeListener() {

            override fun onSwipe(direction: Direction): Boolean {

                when(direction){
                   Direction.up ->
                   {
                       Log.d(TAG, "onSwipe: up")
                       sendCommand("UP")
                       return true
                   }
                     Direction.down ->{
                         Log.d(TAG, "onSwipe: down")
                         sendCommand("DOWN")
                         return true
                     }

                    Direction.left ->
                    {
                        Log.d(TAG, "onSwipe: left")
                        sendCommand("LEFT")
                        return true

                    }
                    Direction.right ->{
                        Log.d(TAG, "onSwipe: right")
                        sendCommand("RIGHT")
                        return true

                    }
                    else -> {
                    }
                }
                return true
            }


        })
        dpadLayout.setOnTouchListener(this)
Horsemint answered 13/2, 2019 at 17:53 Comment(0)
C
0

the extended version of @Fernandour 's answer easy to implement direct with on touch listener no to do extra code… with on touch o'clock ,long press ,double click implementation….

  public class OnSwipeListener implements View.OnTouchListener{
    public enum Direction{up,down,left,right;}
    private GestureDetector gestureDetector;
    private Context context;
    public OnSwipeListener(Context c) {
        this.context = c;

        gestureDetector = new GestureDetector(c, new GestureListener(c));
    }
    public boolean onTouch(final View view, final MotionEvent motionEvent) {

        return gestureDetector.onTouchEvent(motionEvent);
    }

    public void onSwipeRight() {
    }

    public void onSwipeLeft() {
    }

    public void onSwipeUp() {
    }

    public void onSwipeDown() {
    }

    public void onClick() {
    }

    public void onDoubleClick() {
    }

    public void onLongClick() {
    }
    public double getAngle(float x1, float y1, float x2, float y2) {
        double rad = Math.atan2(y1-y2,x2-x1) + Math.PI;
        return (rad*180/Math.PI + 180)%360;
    }

    private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
        Context context;
        public GestureListener(Context c) {
            this.context = c;
        }

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

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            onClick();
            return super.onSingleTapUp(e);
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            onDoubleClick();
            return super.onDoubleTap(e);
        }

        @Override
        public void onLongPress(MotionEvent e) {
            onLongClick();
            super.onLongPress(e);
        }
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            float x1 = e1.getX();
            float y1 = e1.getY();

            float x2 = e2.getX();
            float y2 = e2.getY();

            Direction direction = getDirection(x1,y1,x2,y2);


            return onSwipe(direction);
        }

        public Direction getDirection(float x1, float y1, float x2, float y2){
            double angle = getAngle(x1, y1, x2, y2);
            return fromAngle(angle);
        }

        public  Direction fromAngle(double angle){
            if(inRange(angle, 45, 135)){
                onSwipeUp();
                return Direction.up;
            }
            else if(inRange(angle, 0,45) || inRange(angle, 315, 360)){
                onSwipeRight();
                return Direction.right;
            }
//            else if(inRange(angle, 225, 315)){
//                //onSwipeDown();
//
//            }
            else if(inRange(angle,135, 225)){
                onSwipeLeft();
                return Direction.left;
            }
            else {
                return Direction.down;
            }


        }
         private  boolean inRange(double angle, float init, float end){
            return (angle >= init) && (angle < end);
        }

        public boolean onSwipe(Direction direction){
            return false;
        }
    };
}
Cockerel answered 17/9, 2019 at 10:27 Comment(0)
S
0

Best Way and Simple Way to detect Swipe left, Right, Top, Bottom

  1. First Make One Java Class and implements View.OnTouchListener Add below code in this Class:
    public  class OnSwipeTouchListener implements View.OnTouchListener {
        private final GestureDetector gestureDetector;
        Context context;
        OnSwipeTouchListener(Context ctx, View mainView) {
            gestureDetector = new GestureDetector(ctx, new GestureListener());
            mainView.setOnTouchListener(this);
            context = ctx;
        }
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return gestureDetector.onTouchEvent(event);
        }
        public class GestureListener extends
                GestureDetector.SimpleOnGestureListener {
            private static final int SWIPE_THRESHOLD = 100;
            private static final int SWIPE_VELOCITY_THRESHOLD = 100;
            @Override
            public boolean onDown(MotionEvent e) {
                return true;
            }
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                boolean result = false;
                try {
                    float diffY = e2.getY() - e1.getY();
                    float diffX = e2.getX() - e1.getX();
                    if (Math.abs(diffX) > Math.abs(diffY)) {
                        if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                            if (diffX > 0) {
                                onSwipeRight();
                            } else {
                                onSwipeLeft();
                            }
                            result = true;
                        }
                    }
                    else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffY > 0) {
                            onSwipeBottom();
                        } else {
                            onSwipeTop();
                        }
                        result = true;
                    }
                }
                catch (Exception exception) {
                    exception.printStackTrace();
                }
                return result;
            }
        }
        void onSwipeRight() {
            Toast.makeText(context, "You Swipe Right", Toast.LENGTH_SHORT).show();
            this.onSwipe.swipeRight();
        }
        void onSwipeLeft() {
            Toast.makeText(context, "You Swipe Left", Toast.LENGTH_SHORT).show();
            this.onSwipe.swipeLeft();
        }
        void onSwipeTop() {
            Toast.makeText(context, "You Swipe Up", Toast.LENGTH_SHORT).show();
            this.onSwipe.swipeTop();
        }
        void onSwipeBottom() {
            Toast.makeText(context, "You Swipe Down", Toast.LENGTH_SHORT).show();
            this.onSwipe.swipeBottom();
        }
        interface onSwipeListener {
            void swipeRight();
            void swipeTop();
            void swipeBottom();
            void swipeLeft();
        }
        onSwipeListener onSwipe;
    }

And In Your MainActivity Class Use This Code:

    public class MainActivity extends AppCompatActivity {
        OnSwipeTouchListener onSwipeTouchListener;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            onSwipeTouchListener = new OnSwipeTouchListener(this, findViewById(R.id.relativeLayout));
        }
    }

In activity_main.xml:

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/relativeLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="16dp"
        tools:context=".MainActivity">
       
    </RelativeLayout>
Sluiter answered 6/7, 2020 at 6:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.