I want to get fling
gesture detection working in my Android application.
What I have is a GridLayout
that contains 9 ImageView
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() {}
}
I do it a little different, and wrote an extra detector class that implements the View.onTouchListener
onCreate
is 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!
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;
}
}
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.
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.
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");
}