@@ -57,45 +57,70 @@ public class Scroller {
5757 private boolean mFlywheel ;
5858
5959 private float mVelocity ;
60+ private float mCurrVelocity ;
61+ private int mDistance ;
62+
63+ private float mFlingFriction = ViewConfiguration .getScrollFriction ();
6064
6165 private static final int DEFAULT_DURATION = 250 ;
6266 private static final int SCROLL_MODE = 0 ;
6367 private static final int FLING_MODE = 1 ;
6468
65- private static float DECELERATION_RATE = (float ) (Math .log (0.75 ) / Math .log (0.9 ));
66- private static float ALPHA = 800 ; // pixels / seconds
67- private static float START_TENSION = 0.4f ; // Tension at start: (0.4 * total T, 1.0 * Distance)
68- private static float END_TENSION = 1.0f - START_TENSION ;
69+ private static float DECELERATION_RATE = (float ) (Math .log (0.78 ) / Math .log (0.9 ));
70+ private static final float INFLEXION = 0.35f ; // Tension lines cross at (INFLEXION, 1)
71+ private static final float START_TENSION = 0.5f ;
72+ private static final float END_TENSION = 1.0f ;
73+ private static final float P1 = START_TENSION * INFLEXION ;
74+ private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION );
75+
6976 private static final int NB_SAMPLES = 100 ;
70- private static final float [] SPLINE = new float [NB_SAMPLES + 1 ];
77+ private static final float [] SPLINE_POSITION = new float [NB_SAMPLES + 1 ];
78+ private static final float [] SPLINE_TIME = new float [NB_SAMPLES + 1 ];
7179
7280 private float mDeceleration ;
7381 private final float mPpi ;
7482
83+ // A context-specific coefficient adjusted to physical values.
84+ private float mPhysicalCoeff ;
85+
7586 static {
7687 float x_min = 0.0f ;
77- for (int i = 0 ; i <= NB_SAMPLES ; i ++) {
78- final float t = (float ) i / NB_SAMPLES ;
88+ float y_min = 0.0f ;
89+ for (int i = 0 ; i < NB_SAMPLES ; i ++) {
90+ final float alpha = (float ) i / NB_SAMPLES ;
91+
7992 float x_max = 1.0f ;
8093 float x , tx , coef ;
8194 while (true ) {
8295 x = x_min + (x_max - x_min ) / 2.0f ;
8396 coef = 3.0f * x * (1.0f - x );
84- tx = coef * ((1.0f - x ) * START_TENSION + x * END_TENSION ) + x * x * x ;
85- if (Math .abs (tx - t ) < 1E-5 ) break ;
86- if (tx > t ) x_max = x ;
97+ tx = coef * ((1.0f - x ) * P1 + x * P2 ) + x * x * x ;
98+ if (Math .abs (tx - alpha ) < 1E-5 ) break ;
99+ if (tx > alpha ) x_max = x ;
87100 else x_min = x ;
88101 }
89- final float d = coef + x * x * x ;
90- SPLINE [i ] = d ;
102+ SPLINE_POSITION [i ] = coef * ((1.0f - x ) * START_TENSION + x ) + x * x * x ;
103+
104+ float y_max = 1.0f ;
105+ float y , dy ;
106+ while (true ) {
107+ y = y_min + (y_max - y_min ) / 2.0f ;
108+ coef = 3.0f * y * (1.0f - y );
109+ dy = coef * ((1.0f - y ) * START_TENSION + y ) + y * y * y ;
110+ if (Math .abs (dy - alpha ) < 1E-5 ) break ;
111+ if (dy > alpha ) y_max = y ;
112+ else y_min = y ;
113+ }
114+ SPLINE_TIME [i ] = coef * ((1.0f - y ) * P1 + y * P2 ) + y * y * y ;
91115 }
92- SPLINE [NB_SAMPLES ] = 1.0f ;
116+ SPLINE_POSITION [ NB_SAMPLES ] = SPLINE_TIME [NB_SAMPLES ] = 1.0f ;
93117
94118 // This controls the viscous fluid effect (how much of it)
95119 sViscousFluidScale = 8.0f ;
96120 // must be set to 1.0 (used in viscousFluid())
97121 sViscousFluidNormalize = 1.0f ;
98122 sViscousFluidNormalize = 1.0f / viscousFluid (1.0f );
123+
99124 }
100125
101126 private static float sViscousFluidScale ;
@@ -129,6 +154,8 @@ public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
129154 mPpi = context .getResources ().getDisplayMetrics ().density * 160.0f ;
130155 mDeceleration = computeDeceleration (ViewConfiguration .getScrollFriction ());
131156 mFlywheel = flywheel ;
157+
158+ mPhysicalCoeff = computeDeceleration (0.84f ); // look and feel tuning
132159 }
133160
134161 /**
@@ -140,6 +167,7 @@ public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
140167 */
141168 public final void setFriction (float friction ) {
142169 mDeceleration = computeDeceleration (friction );
170+ mFlingFriction = friction ;
143171 }
144172
145173 private float computeDeceleration (float friction ) {
@@ -202,7 +230,8 @@ public final int getCurrY() {
202230 * negative.
203231 */
204232 public float getCurrVelocity () {
205- return mVelocity - mDeceleration * timePassed () / 2000.0f ;
233+ return mMode == FLING_MODE ?
234+ mCurrVelocity : mVelocity - mDeceleration * timePassed () / 2000.0f ;
206235 }
207236
208237 /**
@@ -269,11 +298,18 @@ public boolean computeScrollOffset() {
269298 case FLING_MODE :
270299 final float t = (float ) timePassed / mDuration ;
271300 final int index = (int ) (NB_SAMPLES * t );
272- final float t_inf = (float ) index / NB_SAMPLES ;
273- final float t_sup = (float ) (index + 1 ) / NB_SAMPLES ;
274- final float d_inf = SPLINE [index ];
275- final float d_sup = SPLINE [index + 1 ];
276- final float distanceCoef = d_inf + (t - t_inf ) / (t_sup - t_inf ) * (d_sup - d_inf );
301+ float distanceCoef = 1.f ;
302+ float velocityCoef = 0.f ;
303+ if (index < NB_SAMPLES ) {
304+ final float t_inf = (float ) index / NB_SAMPLES ;
305+ final float t_sup = (float ) (index + 1 ) / NB_SAMPLES ;
306+ final float d_inf = SPLINE_POSITION [index ];
307+ final float d_sup = SPLINE_POSITION [index + 1 ];
308+ velocityCoef = (d_sup - d_inf ) / (t_sup - t_inf );
309+ distanceCoef = d_inf + (t - t_inf ) * velocityCoef ;
310+ }
311+
312+ mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f ;
277313
278314 mCurrX = mStartX + Math .round (distanceCoef * (mFinalX - mStartX ));
279315 // Pin to mMinX <= mCurrX <= mMaxX
@@ -392,34 +428,49 @@ public void fling(int startX, int startY, int velocityX, int velocityY,
392428 float velocity = FloatMath .sqrt (velocityX * velocityX + velocityY * velocityY );
393429
394430 mVelocity = velocity ;
395- final double l = Math .log (START_TENSION * velocity / ALPHA );
396- mDuration = (int ) (1000.0 * Math .exp (l / (DECELERATION_RATE - 1.0 )));
431+ mDuration = getSplineFlingDuration (velocity );
397432 mStartTime = AnimationUtils .currentAnimationTimeMillis ();
398433 mStartX = startX ;
399434 mStartY = startY ;
400435
401436 float coeffX = velocity == 0 ? 1.0f : velocityX / velocity ;
402437 float coeffY = velocity == 0 ? 1.0f : velocityY / velocity ;
403438
404- int totalDistance =
405- (int ) (ALPHA * Math .exp ( DECELERATION_RATE / ( DECELERATION_RATE - 1.0 ) * l ));
439+ double totalDistance = getSplineFlingDistance ( velocity );
440+ mDistance = (int ) (totalDistance * Math .signum ( velocity ));
406441
407442 mMinX = minX ;
408443 mMaxX = maxX ;
409444 mMinY = minY ;
410445 mMaxY = maxY ;
411446
412- mFinalX = startX + Math .round (totalDistance * coeffX );
447+ mFinalX = startX + ( int ) Math .round (totalDistance * coeffX );
413448 // Pin to mMinX <= mFinalX <= mMaxX
414449 mFinalX = Math .min (mFinalX , mMaxX );
415450 mFinalX = Math .max (mFinalX , mMinX );
416451
417- mFinalY = startY + Math .round (totalDistance * coeffY );
452+ mFinalY = startY + ( int ) Math .round (totalDistance * coeffY );
418453 // Pin to mMinY <= mFinalY <= mMaxY
419454 mFinalY = Math .min (mFinalY , mMaxY );
420455 mFinalY = Math .max (mFinalY , mMinY );
421456 }
422457
458+ private double getSplineDeceleration (float velocity ) {
459+ return Math .log (INFLEXION * Math .abs (velocity ) / (mFlingFriction * mPhysicalCoeff ));
460+ }
461+
462+ private int getSplineFlingDuration (float velocity ) {
463+ final double l = getSplineDeceleration (velocity );
464+ final double decelMinusOne = DECELERATION_RATE - 1.0 ;
465+ return (int ) (1000.0 * Math .exp (l / decelMinusOne ));
466+ }
467+
468+ private double getSplineFlingDistance (float velocity ) {
469+ final double l = getSplineDeceleration (velocity );
470+ final double decelMinusOne = DECELERATION_RATE - 1.0 ;
471+ return mFlingFriction * mPhysicalCoeff * Math .exp (DECELERATION_RATE / decelMinusOne * l );
472+ }
473+
423474 static float viscousFluid (float x )
424475 {
425476 x *= sViscousFluidScale ;
0 commit comments