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     @SuppressWarnings("unused")
     30     private static final String TAG = "FlingController";
     31 
     32     // The fling duration (in milliseconds) when velocity is 1 pixel/second
     33     private static final float FLING_DURATION_PARAM = 50f;
     34     private static final int DECELERATED_FACTOR = 4;
     35 
     36     private int mStartX, mStartY;
     37     private int mMinX, mMinY, mMaxX, mMaxY;
     38     private double mSinAngle;
     39     private double mCosAngle;
     40     private int mDuration;
     41     private int mDistance;
     42     private int mFinalX, mFinalY;
     43 
     44     private int mCurrX, mCurrY;
     45     private double mCurrV;
     46 
     47     public int getFinalX() {
     48         return mFinalX;
     49     }
     50 
     51     public int getFinalY() {
     52         return mFinalY;
     53     }
     54 
     55     public int getDuration() {
     56         return mDuration;
     57     }
     58 
     59     public int getCurrX() {
     60         return mCurrX;
     61 
     62     }
     63 
     64     public int getCurrY() {
     65         return mCurrY;
     66     }
     67 
     68     public int getCurrVelocityX() {
     69         return (int)Math.round(mCurrV * mCosAngle);
     70     }
     71 
     72     public int getCurrVelocityY() {
     73         return (int)Math.round(mCurrV * mSinAngle);
     74     }
     75 
     76     public void fling(int startX, int startY, int velocityX, int velocityY,
     77             int minX, int maxX, int minY, int maxY) {
     78         mStartX = startX;
     79         mStartY = startY;
     80         mMinX = minX;
     81         mMinY = minY;
     82         mMaxX = maxX;
     83         mMaxY = maxY;
     84 
     85         double velocity = Math.hypot(velocityX, velocityY);
     86         mSinAngle = velocityY / velocity;
     87         mCosAngle = velocityX / velocity;
     88         //
     89         // The position formula: x(t) = s + (e - s) * (1 - (1 - t / T) ^ d)
     90         //     velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T
     91         // Thus,
     92         //     v0 = d * (e - s) / T => (e - s) = v0 * T / d
     93         //
     94 
     95         // Ta = T_ref * (Va / V_ref) ^ (1 / (d - 1)); V_ref = 1 pixel/second;
     96         mDuration = (int)Math.round(FLING_DURATION_PARAM
     97                 * Math.pow(Math.abs(velocity), 1.0 / (DECELERATED_FACTOR - 1)));
     98 
     99         // (e - s) = v0 * T / d
    100         mDistance = (int)Math.round(
    101                 velocity * mDuration / DECELERATED_FACTOR / 1000);
    102 
    103         mFinalX = getX(1.0f);
    104         mFinalY = getY(1.0f);
    105     }
    106 
    107     public void computeScrollOffset(float progress) {
    108         progress = Math.min(progress, 1);
    109         float f = 1 - progress;
    110         f = 1 - (float) Math.pow(f, DECELERATED_FACTOR);
    111         mCurrX = getX(f);
    112         mCurrY = getY(f);
    113         mCurrV = getV(progress);
    114     }
    115 
    116     private int getX(float f) {
    117         int r = (int) Math.round(mStartX + f * mDistance * mCosAngle);
    118         if (mCosAngle > 0 && mStartX <= mMaxX) {
    119             r = Math.min(r, mMaxX);
    120         } else if (mCosAngle < 0 && mStartX >= mMinX) {
    121             r = Math.max(r, mMinX);
    122         }
    123         return r;
    124     }
    125 
    126     private int getY(float f) {
    127         int r = (int) Math.round(mStartY + f * mDistance * mSinAngle);
    128         if (mSinAngle > 0 && mStartY <= mMaxY) {
    129             r = Math.min(r, mMaxY);
    130         } else if (mSinAngle < 0 && mStartY >= mMinY) {
    131             r = Math.max(r, mMinY);
    132         }
    133         return r;
    134     }
    135 
    136     private double getV(float progress) {
    137         // velocity formula: v(t) = d * (e - s) * (1 - t / T) ^ (d - 1) / T
    138         return DECELERATED_FACTOR * mDistance * 1000 *
    139                 Math.pow(1 - progress, DECELERATED_FACTOR - 1) / mDuration;
    140     }
    141 }
    142