/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.gallery3d.ui; // This is a customized version of Scroller, with a interface similar to // android.widget.Scroller. It does fling only, not scroll. // // The differences between the this Scroller and the system one are: // // (1) The velocity does not change because of min/max limit. // (2) The duration is different. // (3) The deceleration curve is different. class FlingScroller { @SuppressWarnings("unused") private static final String TAG = "FlingController"; // The fling duration (in milliseconds) when velocity is 1 pixel/second private static final float FLING_DURATION_PARAM = 50f; private static final int DECELERATED_FACTOR = 4; private int mStartX, mStartY; private int mMinX, mMinY, mMaxX, mMaxY; private double mSinAngle; private double mCosAngle; private int mDuration; private int mDistance; private int mFinalX, mFinalY; private int mCurrX, mCurrY; private double mCurrV; public int getFinalX() { return mFinalX; } public int getFinalY() { return mFinalY; } public int getDuration() { return mDuration; } public int getCurrX() { return mCurrX; } public int getCurrY() { return mCurrY; } public int getCurrVelocityX() { return (int)Math.round(mCurrV * mCosAngle); } public int getCurrVelocityY() { return (int)Math.round(mCurrV * mSinAngle); } public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) { mStartX = startX; mStartY = startY; mMinX = minX; mMinY = minY; mMaxX = maxX; mMaxY = maxY; double velocity = Math.hypot(velocityX, velocityY); mSinAngle = velocityY / velocity; mCosAngle = velocityX / velocity; // // The position formula: x(t) = s + (e - s) * (1 - (1 - t / T) ^ d) // velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T // Thus, // v0 = d * (e - s) / T => (e - s) = v0 * T / d // // Ta = T_ref * (Va / V_ref) ^ (1 / (d - 1)); V_ref = 1 pixel/second; mDuration = (int)Math.round(FLING_DURATION_PARAM * Math.pow(Math.abs(velocity), 1.0 / (DECELERATED_FACTOR - 1))); // (e - s) = v0 * T / d mDistance = (int)Math.round( velocity * mDuration / DECELERATED_FACTOR / 1000); mFinalX = getX(1.0f); mFinalY = getY(1.0f); } public void computeScrollOffset(float progress) { progress = Math.min(progress, 1); float f = 1 - progress; f = 1 - (float) Math.pow(f, DECELERATED_FACTOR); mCurrX = getX(f); mCurrY = getY(f); mCurrV = getV(progress); } private int getX(float f) { int r = (int) Math.round(mStartX + f * mDistance * mCosAngle); if (mCosAngle > 0 && mStartX <= mMaxX) { r = Math.min(r, mMaxX); } else if (mCosAngle < 0 && mStartX >= mMinX) { r = Math.max(r, mMinX); } return r; } private int getY(float f) { int r = (int) Math.round(mStartY + f * mDistance * mSinAngle); if (mSinAngle > 0 && mStartY <= mMaxY) { r = Math.min(r, mMaxY); } else if (mSinAngle < 0 && mStartY >= mMinY) { r = Math.max(r, mMinY); } return r; } private double getV(float progress) { // velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T return DECELERATED_FACTOR * mDistance * 1000 * Math.pow(1 - progress, DECELERATED_FACTOR - 1) / mDuration; } }