Fling gesture detection on grid layout Fling gesture detection on grid layout android android

Fling gesture detection on grid layout


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 gridimageView.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.


One of the answers above mentions handling different pixel density but suggests computing the swipe parameters by hand. It is worth noting that you can actually obtain scaled, reasonable values from the system using ViewConfiguration class:

final ViewConfiguration vc = ViewConfiguration.get(getContext());final int swipeMinDistance = vc.getScaledPagingTouchSlop();final int swipeThresholdVelocity = vc.getScaledMinimumFlingVelocity();final int swipeMaxOffPath = vc.getScaledTouchSlop();// (there is also vc.getScaledMaximumFlingVelocity() one could check against)

I noticed that using these values causes the "feel" of fling to be more consistent between the application and rest of system.


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!