Search code examples
androidswipegesture

Making the letter L with OnSwipeTouchListener


I found a good listener class in internet to make an OnSwipeTouchListener. This listener can determinate when the user make a swipe down, up, left or right :

public class OnSwipeTouchListener implements View.OnTouchListener {

    private GestureDetector gestureDetector;

    protected OnSwipeTouchListener(Context c) {
        gestureDetector = new GestureDetector(c, new GestureListener());
    }

    public boolean onTouch(final View view, final MotionEvent motionEvent) {
        return gestureDetector.onTouchEvent(motionEvent);
    }

    private final 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) {
            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();
                        }
                    }
                } else {
                    if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffY > 0) {
                            onSwipeDown();
                        } else {
                            onSwipeUp();
                        }
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return false;
        }
    }

    public void onSwipeRight() {
    }

    public void onSwipeLeft() {
    }

    public void onSwipeUp() {
    }

    public void onSwipeDown() {
    }
}

This is how I use it :

findViewById(R.id.framelayout).setOnTouchListener(new OnSwipeTouchListener(this) {
            @Override
            public void onSwipeDown() {
                Toast.makeText(MainActivity.this, "Down", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onSwipeLeft() {
                Toast.makeText(MainActivity.this, "Left", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onSwipeUp() {
                Toast.makeText(MainActivity.this, "Up", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onSwipeRight() {
                Toast.makeText(MainActivity.this, "Right", Toast.LENGTH_SHORT).show();
            }
        });

Now, I would like to add the function onSwipeL(). This function is when the user make the letter L with the finger, it's like onSwipeDown() + onSwipeRight().

The best would be making the function onSwipeDoubleL(). This is when the user make a double L inverted with his fingers. It's like at the same time making :

  • onSwipeDown() + onSwipeRight()
  • onSwipeDown() + onSwipeLeft()

Is that possible ?


Solution

  • I really can't understand why you need this gesture. But here is my solution. We should catch touch events, so override onTouchEvent method. There we need to read index of fingers which on the screen now.

    If we got more than one touch apply right index for finger which X coordinate more than "left finger". I can't say if I find correct indexes. So I check indexes coordinates.

    In next step we save start point and start to move down. If we got 2 fingers on the screen, check right and left fingers else only left (or right as you wish).

    If we catch event when "down" distance more then minimum and we start move to the left/right, we need to save new start point to calculate distance now from it.

    And wait while user move to the left/right with distance more than min. After all check success by "left" and "right" fingers or just left (one touch).

    We also need to think about inaccuracy(mistakes, error) when move. Users can't move down or left perfect correct, so the solution is highly dependent on this parameter. You have to balance range and accuracy parameters to comfort gesture control.

    The code is not optimized only show the basic idea. Maybe you will find more small solution. Sorry for my English level

    public class MainMenuActivity extends AppCompatActivity {
    
        boolean movingDownL = false;
        boolean movingDownR = false;
        boolean movingLeft = false;
        boolean movingRight = false;
    
        boolean movingSuccessL = false;
        boolean movingSuccessR = false;
    
        // Deviation in pixels from the route (error value)
        int downInaccuracy = 30; // Down
        int lnrInaccuracy = 10; // Left and Right
    
        // Minimum distance to apply move (300 px in down and 100 to the left/right)
        int downMinDistance = 300;
        int lnrMinDistance = 50;
    
        Point oldCoordsL = new Point(0, 0); // Old coordinates left
        Point oldCoordsR = new Point(0, 0); // Old coordinates right
        Point startPointL = new Point(0, 0);
        Point startPointR = new Point(0, 0);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main_menu);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int pIndexL = event.findPointerIndex(event.getPointerId(0));
            int pIndexR = 0;
    
            // If we have more than 1 touch read second finger index
            if(event.getPointerCount() > 1) pIndexR = event.findPointerIndex(event.getPointerId(1));
    
            // Check if we do not mistake when read fingers id
            if(event.getPointerCount() > 1 && event.getX(pIndexL) > event.getX(pIndexR)) {
                int tmp = pIndexR;
                pIndexR = pIndexL;
                pIndexL = tmp;
            }
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    movingDownL = true; // Start moving fingers
                    movingDownR = true;
                    movingSuccessL = false;
                    movingSuccessR = false;
    
                    // Get start point left and right if we need
                    if(event.getPointerCount() > 1) {
                        startPointR = new Point((int) event.getX(pIndexR), (int) event.getY(pIndexR));
                        oldCoordsR = new Point((int) event.getX(pIndexR), (int) event.getY(pIndexR));
                    }
    
                    startPointL = new Point((int) event.getX(pIndexL), (int) event.getY(pIndexL));
                    oldCoordsL = new Point((int) event.getX(pIndexL), (int) event.getY(pIndexL));
                    break;
                case MotionEvent.ACTION_MOVE:
                    // Add right finger handler
                    if(event.getPointerCount() > 1) {
                        if(!movingDownR) {
                            // Check if we still moving to down
                            if(Math.abs(oldCoordsR.x - event.getX(pIndexR)) < downInaccuracy &&
                                    oldCoordsR.y < event.getY(pIndexR)) break;
                            // Start moving to the right
                            if(Math.abs(oldCoordsR.y - event.getY(pIndexR)) < lnrInaccuracy &&
                                    oldCoordsR.x > event.getX(pIndexR) && !movingRight) {
                                movingRight = true;
                                startPointR = new Point(new Point((int)event.getX(pIndexR), (int)event.getY(pIndexR)));
                            }
                        }else {
                            if (Math.abs(oldCoordsR.x - event.getX(pIndexR)) > downInaccuracy ||
                                    oldCoordsR.y < event.getY(pIndexR)) {
                                movingDownR = false;
                                break;
                            } else if(findDistance(startPointR,
                                    new Point((int)event.getX(pIndexR), (int)event.getY(pIndexR))) >= downMinDistance){
                                // Start moving to the left/right
                                movingDownR = false;
                            }
                        }
                    }
    
                    // Left finger handler by default even if we got only one touch
                    // Check if we need move to any side
                    if(!movingDownL) {
                        // Check if we still moving to down
                        if(Math.abs(oldCoordsL.x - event.getX(pIndexL)) < downInaccuracy &&
                                oldCoordsL.y < event.getY(pIndexL)) break;
                        // Start moving to the left
                        if(Math.abs(oldCoordsL.y - event.getY(pIndexL)) < lnrInaccuracy &&
                                oldCoordsL.x < event.getX(pIndexL) && !movingLeft) {
                            movingLeft = true;
                            startPointL = new Point(new Point((int)event.getX(pIndexL), (int)event.getY(pIndexL)));
                        }
                    }else {
                        if (Math.abs(oldCoordsL.x - event.getX(pIndexL)) > downInaccuracy ||
                                oldCoordsL.y > event.getY(pIndexL)) {
                            movingDownL = false;
                            break;
                        } else if(findDistance(startPointL,
                                new Point((int)event.getX(pIndexL), (int)event.getY(pIndexL))) >= downMinDistance){
                            // Start moving to the left/right
                            movingDownL = false;
                        }
                    }
    
                    // Left move handler
                    if(movingLeft) {
                        if (Math.abs(oldCoordsL.y - event.getY(pIndexL)) > lnrInaccuracy ||
                                oldCoordsL.x > event.getX(pIndexL)) {
                            movingLeft = false;
                            break;
                        } else if(findDistance(startPointL,
                                new Point((int)event.getX(pIndexL), (int)event.getY(pIndexL))) >= lnrMinDistance) {
                            movingLeft = false;
                            movingSuccessL = true; // L from left finger is OK
                        }
                    }
    
                    // Right move handler
                    if(movingRight) {
                        if (Math.abs(oldCoordsR.y - event.getY(pIndexR)) > lnrInaccuracy ||
                                oldCoordsR.x < event.getX(pIndexR)) {
                            movingRight = false;
                            break;
                        } else if(findDistance(startPointR,
                                new Point((int)event.getX(pIndexR), (int)event.getY(pIndexR))) >= lnrMinDistance) {
                            movingRight = false;
                            movingSuccessR = true; // L from right finger is OK
                        }
                    }
    
                    if(movingSuccessL && movingSuccessR) {
                        Toast.makeText(this, "Yeah, it's look like double L", Toast.LENGTH_SHORT).show();
                    } else if(movingSuccessL) Toast.makeText(this, "Yeah, it's look like L", Toast.LENGTH_SHORT).show();
    
                    oldCoordsL = new Point((int)event.getX(pIndexL), (int)event.getY(pIndexL));
                    oldCoordsR = new Point((int)event.getX(pIndexR), (int)event.getY(pIndexR));
    
                    break;
                case MotionEvent.ACTION_UP:
                    movingDownL = false;
                    movingDownR = false;
                    movingLeft = false;
                    movingRight = false;
                    break;
                default:
                    return false;
            }
    
            return true;
        }
    
        private double findDistance(Point p1, Point p2) {
            return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
        }
    }