Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.gallery3d.ui;
     18 
     19 
     20 // This is a customized version of Scroller, with a interface similar to
     21 // android.widget.Scroller. It does fling only, not scroll.
     22 //
     23 // The differences between the this Scroller and the system one are:
     24 //
     25 // (1) The velocity does not change because of min/max limit.
     26 // (2) The duration is different.
     27 // (3) The deceleration curve is different.
     28 class FlingScroller {
     29     private static final String TAG = "FlingController";
     30 
     31     // The fling duration (in milliseconds) when velocity is 1 pixel/second
     32     private static final float FLING_DURATION_PARAM = 50f;
     33     private static final int DECELERATED_FACTOR = 4;
     34 
     35     private int mStartX, mStartY;
     36     private int mMinX, mMinY, mMaxX, mMaxY;
     37     private double mSinAngle;
     38     private double mCosAngle;
     39     private int mDuration;
     40     private int mDistance;
     41     private int mFinalX, mFinalY;
     42 
     43     private int mCurrX, mCurrY;
     44     private double mCurrV;
     45 
     46     public int getFinalX() {
     47         return mFinalX;
     48     }
     49 
     50     public int getFinalY() {
     51         return mFinalY;
     52     }
     53 
     54     public int getDuration() {
     55         return mDuration;
     56     }
     57 
     58     public int getCurrX() {
     59         return mCurrX;
     60 
     61     }
     62 
     63     public int getCurrY() {
     64         return mCurrY;
     65     }
     66 
     67     public int getCurrVelocityX() {
     68         return (int)Math.round(mCurrV * mCosAngle);
     69     }
     70 
     71     public int getCurrVelocityY() {
     72         return (int)Math.round(mCurrV * mSinAngle);
     73     }
     74 
     75     public void fling(int startX, int startY, int velocityX, int velocityY,
     76             int minX, int maxX, int minY, int maxY) {
     77         mStartX = startX;
     78         mStartY = startY;
     79         mMinX = minX;
     80         mMinY = minY;
     81         mMaxX = maxX;
     82         mMaxY = maxY;
     83 
     84         double velocity = Math.hypot(velocityX, velocityY);
     85         mSinAngle = velocityY / velocity;
     86         mCosAngle = velocityX / velocity;
     87         //
     88         // The position formula: x(t) = s + (e - s) * (1 - (1 - t / T) ^ d)
     89         //     velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T
     90         // Thus,
     91         //     v0 = d * (e - s) / T => (e - s) = v0 * T / d
     92         //
     93 
     94         // Ta = T_ref * (Va / V_ref) ^ (1 / (d - 1)); V_ref = 1 pixel/second;
     95         mDuration = (int)Math.round(FLING_DURATION_PARAM
     96                 * Math.pow(Math.abs(velocity), 1.0 / (DECELERATED_FACTOR - 1)));
     97 
     98         // (e - s) = v0 * T / d
     99         mDistance = (int)Math.round(
    100                 velocity * mDuration / DECELERATED_FACTOR / 1000);
    101 
    102         mFinalX = getX(1.0f);
    103         mFinalY = getY(1.0f);
    104     }
    105 
    106     public void computeScrollOffset(float progress) {
    107         progress = Math.min(progress, 1);
    108         float f = 1 - progress;
    109         f = 1 - (float) Math.pow(f, DECELERATED_FACTOR);
    110         mCurrX = getX(f);
    111         mCurrY = getY(f);
    112         mCurrV = getV(progress);
    113     }
    114 
    115     private int getX(float f) {
    116         int r = (int) Math.round(mStartX + f * mDistance * mCosAngle);
    117         if (mCosAngle > 0 && mStartX <= mMaxX) {
    118             r = Math.min(r, mMaxX);
    119         } else if (mCosAngle < 0 && mStartX >= mMinX) {
    120             r = Math.max(r, mMinX);
    121         }
    122         return r;
    123     }
    124 
    125     private int getY(float f) {
    126         int r = (int) Math.round(mStartY + f * mDistance * mSinAngle);
    127         if (mSinAngle > 0 && mStartY <= mMaxY) {
    128             r = Math.min(r, mMaxY);
    129         } else if (mSinAngle < 0 && mStartY >= mMinY) {
    130             r = Math.max(r, mMinY);
    131         }
    132         return r;
    133     }
    134 
    135     private double getV(float progress) {
    136         // velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T
    137         return DECELERATED_FACTOR * mDistance * 1000 *
    138                 Math.pow(1 - progress, DECELERATED_FACTOR - 1) / mDuration;
    139     }
    140 }
    141