1b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang/* 2b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * Copyright (C) 2011 The Android Open Source Project 3b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * 4b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License"); 5b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * you may not use this file except in compliance with the License. 6b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * You may obtain a copy of the License at 7b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * 8b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * http://www.apache.org/licenses/LICENSE-2.0 9b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * 10b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * Unless required by applicable law or agreed to in writing, software 11b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS, 12b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * See the License for the specific language governing permissions and 14b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * limitations under the License. 15b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang */ 16b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 17b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Changpackage com.android.gallery3d.ui; 18b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 19b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 20b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// This is a customized version of Scroller, with a interface similar to 21b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// android.widget.Scroller. It does fling only, not scroll. 22b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// 23b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// The differences between the this Scroller and the system one are: 24b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// 25b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// (1) The velocity does not change because of min/max limit. 26b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// (2) The duration is different. 27b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang// (3) The deceleration curve is different. 28b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Changclass FlingScroller { 297817979db0c52ffeacb951625b1e821eba303285Ahbong Chang @SuppressWarnings("unused") 30b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private static final String TAG = "FlingController"; 31b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 32b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // The fling duration (in milliseconds) when velocity is 1 pixel/second 33b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private static final float FLING_DURATION_PARAM = 50f; 34b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private static final int DECELERATED_FACTOR = 4; 35b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 36b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int mStartX, mStartY; 37b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int mMinX, mMinY, mMaxX, mMaxY; 38b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private double mSinAngle; 39b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private double mCosAngle; 40b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int mDuration; 41b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int mDistance; 42b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int mFinalX, mFinalY; 43b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 44b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int mCurrX, mCurrY; 45532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang private double mCurrV; 46b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 47b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public int getFinalX() { 48b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return mFinalX; 49b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 50b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 51b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public int getFinalY() { 52b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return mFinalY; 53b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 54b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 55b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public int getDuration() { 56b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return mDuration; 57b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 58b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 59b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public int getCurrX() { 60b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return mCurrX; 61b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 62b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 63b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 64b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public int getCurrY() { 65b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang return mCurrY; 66b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 67b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 68532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang public int getCurrVelocityX() { 69532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang return (int)Math.round(mCurrV * mCosAngle); 70532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 71532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 72532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang public int getCurrVelocityY() { 73532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang return (int)Math.round(mCurrV * mSinAngle); 74532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 75532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 76b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public void fling(int startX, int startY, int velocityX, int velocityY, 77b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang int minX, int maxX, int minY, int maxY) { 78b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mStartX = startX; 79b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mStartY = startY; 80b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mMinX = minX; 81b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mMinY = minY; 82b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mMaxX = maxX; 83b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mMaxY = maxY; 84b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 85b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang double velocity = Math.hypot(velocityX, velocityY); 86b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mSinAngle = velocityY / velocity; 87b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mCosAngle = velocityX / velocity; 88b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 89b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // The position formula: x(t) = s + (e - s) * (1 - (1 - t / T) ^ d) 90b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T 91b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // Thus, 92b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // v0 = d * (e - s) / T => (e - s) = v0 * T / d 93b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // 94b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 95b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // Ta = T_ref * (Va / V_ref) ^ (1 / (d - 1)); V_ref = 1 pixel/second; 96b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mDuration = (int)Math.round(FLING_DURATION_PARAM 97b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang * Math.pow(Math.abs(velocity), 1.0 / (DECELERATED_FACTOR - 1))); 98b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 99b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang // (e - s) = v0 * T / d 100b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mDistance = (int)Math.round( 101b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang velocity * mDuration / DECELERATED_FACTOR / 1000); 102b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 103b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mFinalX = getX(1.0f); 104b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mFinalY = getY(1.0f); 105b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 106b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 107b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang public void computeScrollOffset(float progress) { 108b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang progress = Math.min(progress, 1); 109b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang float f = 1 - progress; 110b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang f = 1 - (float) Math.pow(f, DECELERATED_FACTOR); 111b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mCurrX = getX(f); 112b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang mCurrY = getY(f); 113532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang mCurrV = getV(progress); 114b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 115b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 116b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int getX(float f) { 1178f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang int r = (int) Math.round(mStartX + f * mDistance * mCosAngle); 1188f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang if (mCosAngle > 0 && mStartX <= mMaxX) { 1198f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang r = Math.min(r, mMaxX); 1208f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang } else if (mCosAngle < 0 && mStartX >= mMinX) { 1218f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang r = Math.max(r, mMinX); 1228f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang } 1238f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang return r; 124b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 125b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang 126b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang private int getY(float f) { 1278f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang int r = (int) Math.round(mStartY + f * mDistance * mSinAngle); 1288f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang if (mSinAngle > 0 && mStartY <= mMaxY) { 1298f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang r = Math.min(r, mMaxY); 1308f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang } else if (mSinAngle < 0 && mStartY >= mMinY) { 1318f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang r = Math.max(r, mMinY); 1328f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang } 1338f568da373699781beb11cfa46a46c5871288353Chih-Chung Chang return r; 134b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang } 135532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang 136532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang private double getV(float progress) { 137532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang // velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T 138532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang return DECELERATED_FACTOR * mDistance * 1000 * 139532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang Math.pow(1 - progress, DECELERATED_FACTOR - 1) / mDuration; 140532d93caddc91a7aa33ca113adbc0b8255d498ebChih-Chung Chang } 141b3aab90bb37aa9cc60be32e05678ee55d6575ee8Chih-Chung Chang} 142