org.holoeverywhere.widget.Scroller Maven / Gradle / Ivy
package org.holoeverywhere.widget;
import android.content.Context;
import android.hardware.SensorManager;
import android.os.Build;
import android.util.FloatMath;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
public class Scroller {
private static float ALPHA = 800;
private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math
.log(0.9));
private static final int DEFAULT_DURATION = 250;
private static float END_TENSION = 0.6f;
private static final int FLING_MODE = 1;
private static final int NB_SAMPLES = 100;
private static final int SCROLL_MODE = 0;
private static final float[] SPLINE = new float[Scroller.NB_SAMPLES + 1];
private static float START_TENSION = 0.4f;
private static float sViscousFluidNormalize;
private static float sViscousFluidScale;
static {
float x_min = 0.0f;
for (int i = 0; i <= Scroller.NB_SAMPLES; i++) {
final float t = (float) i / Scroller.NB_SAMPLES;
float x_max = 1.0f;
float x, tx, coef;
while (true) {
x = x_min + (x_max - x_min) / 2.0f;
coef = 3.0f * x * (1.0f - x);
tx = coef
* ((1.0f - x) * Scroller.START_TENSION + x
* Scroller.END_TENSION) + x * x * x;
if (Math.abs(tx - t) < 1E-5) {
break;
}
if (tx > t) {
x_max = x;
} else {
x_min = x;
}
}
final float d = coef + x * x * x;
Scroller.SPLINE[i] = d;
}
Scroller.SPLINE[Scroller.NB_SAMPLES] = 1.0f;
Scroller.sViscousFluidScale = 8.0f;
Scroller.sViscousFluidNormalize = 1.0f;
Scroller.sViscousFluidNormalize = 1.0f / Scroller.viscousFluid(1.0f);
}
static float viscousFluid(float x) {
x *= Scroller.sViscousFluidScale;
if (x < 1.0f) {
x -= 1.0f - (float) Math.exp(-x);
} else {
float start = 0.36787944117f; // 1/e == exp(-1)
x = 1.0f - (float) Math.exp(1.0f - x);
x = start + x * (1.0f - start);
}
x *= Scroller.sViscousFluidNormalize;
return x;
}
private int mCurrX;
private int mCurrY;
private float mDeceleration;
private float mDeltaX;
private float mDeltaY;
private int mDuration;
private float mDurationReciprocal;
private int mFinalX;
private int mFinalY;
private boolean mFinished;
private boolean mFlywheel;
private Interpolator mInterpolator;
private int mMaxX;
private int mMaxY;
private int mMinX;
private int mMinY;
private int mMode;
private final float mPpi;
private long mStartTime;
private int mStartX;
private int mStartY;
private float mVelocity;
public Scroller(Context context) {
this(context, null);
}
public Scroller(Context context, Interpolator interpolator) {
this(
context,
interpolator,
context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
}
public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
mFinished = true;
mInterpolator = interpolator;
mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
mDeceleration = computeDeceleration(ViewConfiguration
.getScrollFriction());
mFlywheel = flywheel;
}
public void abortAnimation() {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
private float computeDeceleration(float friction) {
return SensorManager.GRAVITY_EARTH * 39.37f * mPpi * friction;
}
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
float x = timePassed * mDurationReciprocal;
if (mInterpolator == null) {
x = Scroller.viscousFluid(x);
} else {
x = mInterpolator.getInterpolation(x);
}
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
final float t = (float) timePassed / mDuration;
final int index = (int) (Scroller.NB_SAMPLES * t);
final float t_inf = (float) index / Scroller.NB_SAMPLES;
final float t_sup = (float) (index + 1) / Scroller.NB_SAMPLES;
final float d_inf = Scroller.SPLINE[index];
final float d_sup = Scroller.SPLINE[index + 1];
final float distanceCoef = d_inf + (t - t_inf)
/ (t_sup - t_inf) * (d_sup - d_inf);
mCurrX = mStartX
+ Math.round(distanceCoef * (mFinalX - mStartX));
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY
+ Math.round(distanceCoef * (mFinalY - mStartY));
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
} else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
public void extendDuration(int extend) {
int passed = timePassed();
mDuration = passed + extend;
mDurationReciprocal = 1.0f / mDuration;
mFinished = false;
}
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
if (mFlywheel && !mFinished) {
float oldVel = getCurrVelocity();
float dx = mFinalX - mStartX;
float dy = mFinalY - mStartY;
float hyp = FloatMath.sqrt(dx * dx + dy * dy);
float ndx = dx / hyp;
float ndy = dy / hyp;
float oldVelocityX = ndx * oldVel;
float oldVelocityY = ndy * oldVel;
if (Math.signum(velocityX) == Math.signum(oldVelocityX)
&& Math.signum(velocityY) == Math.signum(oldVelocityY)) {
velocityX += oldVelocityX;
velocityY += oldVelocityY;
}
}
mMode = Scroller.FLING_MODE;
mFinished = false;
float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY
* velocityY);
mVelocity = velocity;
final double l = Math.log(Scroller.START_TENSION * velocity
/ Scroller.ALPHA);
mDuration = (int) (1000.0 * Math.exp(l
/ (Scroller.DECELERATION_RATE - 1.0)));
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
int totalDistance = (int) (Scroller.ALPHA * Math
.exp(Scroller.DECELERATION_RATE
/ (Scroller.DECELERATION_RATE - 1.0) * l));
mMinX = minX;
mMaxX = maxX;
mMinY = minY;
mMaxY = maxY;
mFinalX = startX + Math.round(totalDistance * coeffX);
mFinalX = Math.min(mFinalX, mMaxX);
mFinalX = Math.max(mFinalX, mMinX);
mFinalY = startY + Math.round(totalDistance * coeffY);
mFinalY = Math.min(mFinalY, mMaxY);
mFinalY = Math.max(mFinalY, mMinY);
}
public final void forceFinished(boolean finished) {
mFinished = finished;
}
public float getCurrVelocity() {
return mVelocity - mDeceleration * timePassed() / 2000.0f;
}
public final int getCurrX() {
return mCurrX;
}
public final int getCurrY() {
return mCurrY;
}
public final int getDuration() {
return mDuration;
}
public final int getFinalX() {
return mFinalX;
}
public final int getFinalY() {
return mFinalY;
}
public final int getStartX() {
return mStartX;
}
public final int getStartY() {
return mStartY;
}
public final boolean isFinished() {
return mFinished;
}
public boolean isScrollingInDirection(float xvel, float yvel) {
return !mFinished
&& Math.signum(xvel) == Math.signum(mFinalX - mStartX)
&& Math.signum(yvel) == Math.signum(mFinalY - mStartY);
}
public void setFinalX(int newX) {
mFinalX = newX;
mDeltaX = mFinalX - mStartX;
mFinished = false;
}
public void setFinalY(int newY) {
mFinalY = newY;
mDeltaY = mFinalY - mStartY;
mFinished = false;
}
public final void setFriction(float friction) {
mDeceleration = computeDeceleration(friction);
}
public void startScroll(int startX, int startY, int dx, int dy) {
startScroll(startX, startY, dx, dy, Scroller.DEFAULT_DURATION);
}
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = Scroller.SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / mDuration;
}
public int timePassed() {
return (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy