I'm writing my own image viewer that enables users to swipe left\right to see the next\previous image. I want to animate the image change according to the fling velocity.
To detect a fling gesture and its velocity, I followed this basic gesture detection and did as the accepted answer suggested:
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;
}
}
The problem is that I get different velocity values (velocityX & velocityY) for different Android devices with same density. Basically it means that in one device the swipe feels slow and not-sensitive, and other devices feels too sensitive.
I thought it might have something to do with the "default" maximum velocity on the device, that can be fetched using -
ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity()
The results on different devices with same density aren't the same as expected:
Samsung S I 480x800 density 1.5 Android 2.1
getScaledMinimumFlingVelocity(): 75
getScaledMaximumFlingVelocity(): 3600
ViewConfiguration.MAXIMUM_FLING_VELOCITY = 4000
HTC Desire 480x800 density 1.5 Android 2.3.3
getScaledMinimumFlingVelocity(): 75
getScaledMaximumFlingVelocity(): 6000
ViewConfiguration.MAXIMUM_FLING_VELOCITY = 4000
Samsung S III mini 480x800 density 1.5 Android 4.1
getScaledMinimumFlingVelocity(): 75
getScaledMaximumFlingVelocity(): 12000
ViewConfiguration.MAXIMUM_FLING_VELOCITY = 8000
You can also see that ViewConfiguration has 2 different values for MAXIMUM_FLING_VELOCITY
in Android below 4.0 and above.
How come different devices with same density and almost same api level don't have the same max velocity? How can I get the same experience on different devices when a user makes a swipe gesture? Can I use the max velocity data to make a uniform experience across all Android devices?
I had a similar problem. Instead of working directly with the max and min fling velocities from ViewConfiguration
, you can normalize the velocity to a value between 0 and 1.
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float maxFlingVelocity = ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity();
float velocityPercentX = velocityX / maxFlingVelocity; // the percent is a value in the range of (0, 1]
float normalizedVelocityX = velocityPercentX * PIXELS_PER_SECOND; // where PIXELS_PER_SECOND is a device-independent measurement
In other words, velocityPercentX
gives you the "power" of the fling as a percent, and normalizedVelocityX
is the velocity in terms of your application logic (such as an image width in device-independent pixels).