Fling gesture detection on grid layout

前端 未结 18 1593
遥遥无期
遥遥无期 2020-11-21 04:38

I want to get fling gesture detection working in my Android application.

What I have is a GridLayout that contains 9 ImageView

相关标签:
18条回答
  • 2020-11-21 05:19

    This is a combined answer of the two answers at top, if anyone wants a working implementation.

    package com.yourapplication;
    
    import android.content.Context;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewConfiguration;
    
    public abstract class OnSwipeListener implements View.OnTouchListener {
    
        private final GestureDetector gestureDetector;
    
        public OnSwipeListener(Context context){
            gestureDetector = new GestureDetector(context, new OnSwipeGestureListener(context));
            gestureDetector.setIsLongpressEnabled(false);
        }
    
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            return gestureDetector.onTouchEvent(event);
        }
    
        private final class OnSwipeGestureListener extends GestureDetector.SimpleOnGestureListener {
    
            private final int minSwipeDelta;
            private final int minSwipeVelocity;
            private final int maxSwipeVelocity;
    
            private OnSwipeGestureListener(Context context) {
                ViewConfiguration configuration = ViewConfiguration.get(context);
                // We think a swipe scrolls a full page.
                //minSwipeDelta = configuration.getScaledTouchSlop();
                minSwipeDelta = configuration.getScaledPagingTouchSlop();
                minSwipeVelocity = configuration.getScaledMinimumFlingVelocity();
                maxSwipeVelocity = configuration.getScaledMaximumFlingVelocity();
            }
    
            @Override
            public boolean onDown(MotionEvent event) {
                // Return true because we want system to report subsequent events to us.
                return true;
            }
    
            // NOTE: see http://stackoverflow.com/questions/937313/android-basic-gesture-detection
            @Override
            public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX,
                                   float velocityY) {
    
                boolean result = false;
                try {
                    float deltaX = event2.getX() - event1.getX();
                    float deltaY = event2.getY() - event1.getY();
                    float absVelocityX = Math.abs(velocityX);
                    float absVelocityY = Math.abs(velocityY);
                    float absDeltaX = Math.abs(deltaX);
                    float absDeltaY = Math.abs(deltaY);
                    if (absDeltaX > absDeltaY) {
                        if (absDeltaX > minSwipeDelta && absVelocityX > minSwipeVelocity
                                && absVelocityX < maxSwipeVelocity) {
                            if (deltaX < 0) {
                                onSwipeLeft();
                            } else {
                                onSwipeRight();
                            }
                        }
                        result = true;
                    } else if (absDeltaY > minSwipeDelta && absVelocityY > minSwipeVelocity
                            && absVelocityY < maxSwipeVelocity) {
                        if (deltaY < 0) {
                            onSwipeTop();
                        } else {
                            onSwipeBottom();
                        }
                    }
                    result = true;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return result;
            }
        }
    
        public void onSwipeLeft() {}
    
        public void onSwipeRight() {}
    
        public void onSwipeTop() {}
    
        public void onSwipeBottom() {}
    }
    
    0 讨论(0)
  • 2020-11-21 05:22

    I do it a little different, and wrote an extra detector class that implements the View.onTouchListener

    onCreateis simply add it to the lowest layout like this:

    ActivitySwipeDetector activitySwipeDetector = new ActivitySwipeDetector(this);
    lowestLayout = (RelativeLayout)this.findViewById(R.id.lowestLayout);
    lowestLayout.setOnTouchListener(activitySwipeDetector);
    

    where id.lowestLayout is the id.xxx for the view lowest in the layout hierarchy and lowestLayout is declared as a RelativeLayout

    And then there is the actual activity swipe detector class:

    public class ActivitySwipeDetector implements View.OnTouchListener {
    
    static final String logTag = "ActivitySwipeDetector";
    private Activity activity;
    static final int MIN_DISTANCE = 100;
    private float downX, downY, upX, upY;
    
    public ActivitySwipeDetector(Activity activity){
        this.activity = activity;
    }
    
    public void onRightSwipe(){
        Log.i(logTag, "RightToLeftSwipe!");
        activity.doSomething();
    }
    
    public void onLeftSwipe(){
        Log.i(logTag, "LeftToRightSwipe!");
        activity.doSomething();
    }
    
    public void onDownSwipe(){
        Log.i(logTag, "onTopToBottomSwipe!");
        activity.doSomething();
    }
    
    public void onUpSwipe(){
        Log.i(logTag, "onBottomToTopSwipe!");
        activity.doSomething();
    }
    
    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN: {
                downX = event.getX();
                downY = event.getY();
                return true;
            }
            case MotionEvent.ACTION_UP: {
                upX = event.getX();
                upY = event.getY();
    
                float deltaX = downX - upX;
                float deltaY = downY - upY;
    
           // swipe horizontal?
            if(Math.abs(deltaX) > Math.abs(deltaY))
            {
                if(Math.abs(deltaX) > MIN_DISTANCE){
                    // left or right
                    if(deltaX > 0) { this.onRightSwipe(); return true; }
                    if(deltaX < 0) { this.onLeftSwipe(); return true; }
                }
                else {
                        Log.i(logTag, "Horizontal Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                        return false; // We don't consume the event
                }
            }
            // swipe vertical?
            else 
            {
                if(Math.abs(deltaY) > MIN_DISTANCE){
                    // top or down
                    if(deltaY < 0) { this.onDownSwipe(); return true; }
                    if(deltaY > 0) { this.onUpSwipe(); return true; }
                }
                else {
                        Log.i(logTag, "Vertical Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                        return false; // We don't consume the event
                }
            }
    
                return true;
            }
        }
        return false;
    }
    
    }
    

    Works really good for me!

    0 讨论(0)
  • 2020-11-21 05:22

    I slightly modified and repaired solution from Thomas Fankhauser

    Whole system consists from two files, SwipeInterface and ActivitySwipeDetector


    SwipeInterface.java

    import android.view.View;
    
    public interface SwipeInterface {
    
        public void bottom2top(View v);
    
        public void left2right(View v);
    
        public void right2left(View v);
    
        public void top2bottom(View v);
    
    }
    

    Detector

    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    public class ActivitySwipeDetector implements View.OnTouchListener {
    
        static final String logTag = "ActivitySwipeDetector";
        private SwipeInterface activity;
        static final int MIN_DISTANCE = 100;
        private float downX, downY, upX, upY;
    
        public ActivitySwipeDetector(SwipeInterface activity){
            this.activity = activity;
        }
    
        public void onRightToLeftSwipe(View v){
            Log.i(logTag, "RightToLeftSwipe!");
            activity.right2left(v);
        }
    
        public void onLeftToRightSwipe(View v){
            Log.i(logTag, "LeftToRightSwipe!");
            activity.left2right(v);
        }
    
        public void onTopToBottomSwipe(View v){
            Log.i(logTag, "onTopToBottomSwipe!");
            activity.top2bottom(v);
        }
    
        public void onBottomToTopSwipe(View v){
            Log.i(logTag, "onBottomToTopSwipe!");
            activity.bottom2top(v);
        }
    
        public boolean onTouch(View v, MotionEvent event) {
            switch(event.getAction()){
            case MotionEvent.ACTION_DOWN: {
                downX = event.getX();
                downY = event.getY();
                return true;
            }
            case MotionEvent.ACTION_UP: {
                upX = event.getX();
                upY = event.getY();
    
                float deltaX = downX - upX;
                float deltaY = downY - upY;
    
                // swipe horizontal?
                if(Math.abs(deltaX) > MIN_DISTANCE){
                    // left or right
                    if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; }
                    if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; }
                }
                else {
                    Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                }
    
                // swipe vertical?
                if(Math.abs(deltaY) > MIN_DISTANCE){
                    // top or down
                    if(deltaY < 0) { this.onTopToBottomSwipe(v); return true; }
                    if(deltaY > 0) { this.onBottomToTopSwipe(v); return true; }
                }
                else {
                    Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
                    v.performClick();
                }
            }
            }
            return false;
        }
    
    }
    

    it is used like this:

    ActivitySwipeDetector swipe = new ActivitySwipeDetector(this);
    LinearLayout swipe_layout = (LinearLayout) findViewById(R.id.swipe_layout);
    swipe_layout.setOnTouchListener(swipe);
    

    And in implementing Activity you need to implement methods from SwipeInterface, and you can find out on which View the Swipe Event was called.

    @Override
    public void left2right(View v) {
        switch(v.getId()){
            case R.id.swipe_layout:
                // do your stuff here
            break;
        }       
    }
    
    0 讨论(0)
  • 2020-11-21 05:23

    Thanks to Code Shogun, whose code I adapted to my situation.

    Let your activity implementOnClickListener as usual:

    public class SelectFilterActivity extends Activity implements OnClickListener {
    
      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;
      private GestureDetector gestureDetector;
      View.OnTouchListener gestureListener;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        /* ... */
    
        // Gesture detection
        gestureDetector = new GestureDetector(this, new MyGestureDetector());
        gestureListener = new View.OnTouchListener() {
          public boolean onTouch(View v, MotionEvent event) {
            return gestureDetector.onTouchEvent(event);
          }
        };
    
      }
    
      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) {
              Toast.makeText(SelectFilterActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show();
            } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
              Toast.makeText(SelectFilterActivity.this, "Right Swipe", Toast.LENGTH_SHORT).show();
            }
          } catch (Exception e) {
             // nothing
          }
          return false;
        }
    
        @Override
        public boolean onDown(MotionEvent e) {
          return true;
        }
      }
    }
    

    Attach your gesture listener to all the views you add to the main layout;

    // Do this for each view added to the grid
    imageView.setOnClickListener(SelectFilterActivity.this); 
    imageView.setOnTouchListener(gestureListener);
    

    Watch in awe as your overridden methods are hit, both the onClick(View v) of the activity and the onFling of the gesture listener.

    public void onClick(View v) {
      Filter f = (Filter) v.getTag();
      FilterFullscreenActivity.show(this, input, f);
    }
    

    The post 'fling' dance is optional but encouraged.

    0 讨论(0)
  • 2020-11-21 05:24

    There's some proposition over the web (and this page) to use ViewConfiguration.getScaledTouchSlop() to have a device-scaled value for SWIPE_MIN_DISTANCE.

    getScaledTouchSlop() is intended for the "scrolling threshold" distance, not swipe. The scrolling threshold distance has to be smaller than a "swing between page" threshold distance. For example, this function returns 12 pixels on my Samsung GS2, and the examples quoted in this page are around 100 pixels.

    With API Level 8 (Android 2.2, Froyo), you've got getScaledPagingTouchSlop(), intended for page swipe. On my device, it returns 24 (pixels). So if you're on API Level < 8, I think "2 * getScaledTouchSlop()" should be the "standard" swipe threshold. But users of my application with small screens told me that it was too few... As on my application, you can scroll vertically, and change page horizontally. With the proposed value, they sometimes change page instead of scrolling.

    0 讨论(0)
  • 2020-11-21 05:24

    I know its too late to answer but Still I am posting Swipe Detection for ListView that How to use Swipe Touch Listener in ListView Item.

    Refrence: Exterminator13(one of answer in this page)

    Make one ActivitySwipeDetector.class

    package com.example.wocketapp;
    
    import android.content.Context;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewConfiguration;
    
    public class ActivitySwipeDetector implements View.OnTouchListener 
    {
        static final String logTag = "SwipeDetector";
        private SwipeInterface activity;
        private float downX, downY;
        private long timeDown;
        private final float MIN_DISTANCE;
        private final int VELOCITY;
        private final float MAX_OFF_PATH;
    
        public ActivitySwipeDetector(Context context, SwipeInterface activity)
        {
            this.activity = activity;
            final ViewConfiguration vc = ViewConfiguration.get(context);
            DisplayMetrics dm = context.getResources().getDisplayMetrics();
            MIN_DISTANCE = vc.getScaledPagingTouchSlop() * dm.density;
            VELOCITY = vc.getScaledMinimumFlingVelocity();
            MAX_OFF_PATH = MIN_DISTANCE * 2;
        }
    
        public void onRightToLeftSwipe(View v) 
        {
            Log.i(logTag, "RightToLeftSwipe!");
            activity.onRightToLeft(v);
        }
    
        public void onLeftToRightSwipe(View v) 
        {
            Log.i(logTag, "LeftToRightSwipe!");
            activity.onLeftToRight(v);
        }
    
        public boolean onTouch(View v, MotionEvent event) 
        {
            switch (event.getAction()) 
            {
                case MotionEvent.ACTION_DOWN:
                {
                    Log.d("onTouch", "ACTION_DOWN");
                    timeDown = System.currentTimeMillis();
                    downX = event.getX();
                    downY = event.getY();
                    v.getParent().requestDisallowInterceptTouchEvent(false);
                    return true;
                }
    
            case MotionEvent.ACTION_MOVE:
                {
                    float y_up = event.getY();
                    float deltaY = y_up - downY;
                    float absDeltaYMove = Math.abs(deltaY);
    
                    if (absDeltaYMove > 60) 
                    {
                        v.getParent().requestDisallowInterceptTouchEvent(false);
                    } 
                    else
                    {
                        v.getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }
    
                break;
    
                case MotionEvent.ACTION_UP: 
                {
                    Log.d("onTouch", "ACTION_UP");
                    long timeUp = System.currentTimeMillis();
                    float upX = event.getX();
                    float upY = event.getY();
    
                    float deltaX = downX - upX;
                    float absDeltaX = Math.abs(deltaX);
                    float deltaY = downY - upY;
                    float absDeltaY = Math.abs(deltaY);
    
                    long time = timeUp - timeDown;
    
                    if (absDeltaY > MAX_OFF_PATH) 
                    {
                        Log.e(logTag, String.format(
                                "absDeltaY=%.2f, MAX_OFF_PATH=%.2f", absDeltaY,
                                MAX_OFF_PATH));
                        return v.performClick();
                    }
    
                    final long M_SEC = 1000;
                    if (absDeltaX > MIN_DISTANCE && absDeltaX > time * VELOCITY / M_SEC) 
                    {
                         v.getParent().requestDisallowInterceptTouchEvent(true);
                        if (deltaX < 0) 
                        {
                            this.onLeftToRightSwipe(v);
                            return true;
                        }
                        if (deltaX > 0) 
                        {
                            this.onRightToLeftSwipe(v);
                            return true;
                        }
                    }
                    else 
                    {
                        Log.i(logTag,
                                String.format(
                                        "absDeltaX=%.2f, MIN_DISTANCE=%.2f, absDeltaX > MIN_DISTANCE=%b",
                                        absDeltaX, MIN_DISTANCE,
                                        (absDeltaX > MIN_DISTANCE)));
                        Log.i(logTag,
                                String.format(
                                        "absDeltaX=%.2f, time=%d, VELOCITY=%d, time*VELOCITY/M_SEC=%d, absDeltaX > time * VELOCITY / M_SEC=%b",
                                        absDeltaX, time, VELOCITY, time * VELOCITY
                                                / M_SEC, (absDeltaX > time * VELOCITY
                                                / M_SEC)));
                    }
    
                     v.getParent().requestDisallowInterceptTouchEvent(false);
    
                }
            }
            return false;
        }
        public interface SwipeInterface 
        {
    
            public void onLeftToRight(View v);
    
            public void onRightToLeft(View v);
        }
    
    }
    

    Call it from your activity class like this:

    yourLayout.setOnTouchListener(new ActivitySwipeDetector(this, your_activity.this));
    

    And Don't forget to implement SwipeInterface which will give you two @override methods:

        @Override
        public void onLeftToRight(View v) 
        {
            Log.e("TAG", "L to R");
        }
    
        @Override
        public void onRightToLeft(View v) 
        {
            Log.e("TAG", "R to L");
        }
    
    0 讨论(0)
提交回复
热议问题