Home | History | Annotate | Download | only in animation
      1 /*
      2  * Copyright (C) 2010 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 android.animation;
     18 
     19 import java.util.Arrays;
     20 import java.util.List;
     21 
     22 import android.animation.Keyframe.IntKeyframe;
     23 import android.animation.Keyframe.FloatKeyframe;
     24 import android.animation.Keyframe.ObjectKeyframe;
     25 import android.graphics.Path;
     26 import android.util.Log;
     27 
     28 /**
     29  * This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
     30  * values between those keyframes for a given animation. The class internal to the animation
     31  * package because it is an implementation detail of how Keyframes are stored and used.
     32  */
     33 class KeyframeSet implements Keyframes {
     34 
     35     int mNumKeyframes;
     36 
     37     Keyframe mFirstKeyframe;
     38     Keyframe mLastKeyframe;
     39     TimeInterpolator mInterpolator; // only used in the 2-keyframe case
     40     List<Keyframe> mKeyframes; // only used when there are not 2 keyframes
     41     TypeEvaluator mEvaluator;
     42 
     43 
     44     public KeyframeSet(Keyframe... keyframes) {
     45         mNumKeyframes = keyframes.length;
     46         // immutable list
     47         mKeyframes = Arrays.asList(keyframes);
     48         mFirstKeyframe = keyframes[0];
     49         mLastKeyframe = keyframes[mNumKeyframes - 1];
     50         mInterpolator = mLastKeyframe.getInterpolator();
     51     }
     52 
     53     /**
     54      * If subclass has variables that it calculates based on the Keyframes, it should reset them
     55      * when this method is called because Keyframe contents might have changed.
     56      */
     57     @Override
     58     public void invalidateCache() {
     59     }
     60 
     61     public List<Keyframe> getKeyframes() {
     62         return mKeyframes;
     63     }
     64 
     65     public static KeyframeSet ofInt(int... values) {
     66         int numKeyframes = values.length;
     67         IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
     68         if (numKeyframes == 1) {
     69             keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
     70             keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
     71         } else {
     72             keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
     73             for (int i = 1; i < numKeyframes; ++i) {
     74                 keyframes[i] =
     75                         (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
     76             }
     77         }
     78         return new IntKeyframeSet(keyframes);
     79     }
     80 
     81     public static KeyframeSet ofFloat(float... values) {
     82         boolean badValue = false;
     83         int numKeyframes = values.length;
     84         FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
     85         if (numKeyframes == 1) {
     86             keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
     87             keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
     88             if (Float.isNaN(values[0])) {
     89                 badValue = true;
     90             }
     91         } else {
     92             keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
     93             for (int i = 1; i < numKeyframes; ++i) {
     94                 keyframes[i] =
     95                         (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
     96                 if (Float.isNaN(values[i])) {
     97                     badValue = true;
     98                 }
     99             }
    100         }
    101         if (badValue) {
    102             Log.w("Animator", "Bad value (NaN) in float animator");
    103         }
    104         return new FloatKeyframeSet(keyframes);
    105     }
    106 
    107     public static KeyframeSet ofKeyframe(Keyframe... keyframes) {
    108         // if all keyframes of same primitive type, create the appropriate KeyframeSet
    109         int numKeyframes = keyframes.length;
    110         boolean hasFloat = false;
    111         boolean hasInt = false;
    112         boolean hasOther = false;
    113         for (int i = 0; i < numKeyframes; ++i) {
    114             if (keyframes[i] instanceof FloatKeyframe) {
    115                 hasFloat = true;
    116             } else if (keyframes[i] instanceof IntKeyframe) {
    117                 hasInt = true;
    118             } else {
    119                 hasOther = true;
    120             }
    121         }
    122         if (hasFloat && !hasInt && !hasOther) {
    123             FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes];
    124             for (int i = 0; i < numKeyframes; ++i) {
    125                 floatKeyframes[i] = (FloatKeyframe) keyframes[i];
    126             }
    127             return new FloatKeyframeSet(floatKeyframes);
    128         } else if (hasInt && !hasFloat && !hasOther) {
    129             IntKeyframe intKeyframes[] = new IntKeyframe[numKeyframes];
    130             for (int i = 0; i < numKeyframes; ++i) {
    131                 intKeyframes[i] = (IntKeyframe) keyframes[i];
    132             }
    133             return new IntKeyframeSet(intKeyframes);
    134         } else {
    135             return new KeyframeSet(keyframes);
    136         }
    137     }
    138 
    139     public static KeyframeSet ofObject(Object... values) {
    140         int numKeyframes = values.length;
    141         ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];
    142         if (numKeyframes == 1) {
    143             keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
    144             keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
    145         } else {
    146             keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
    147             for (int i = 1; i < numKeyframes; ++i) {
    148                 keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
    149             }
    150         }
    151         return new KeyframeSet(keyframes);
    152     }
    153 
    154     public static PathKeyframes ofPath(Path path) {
    155         return new PathKeyframes(path);
    156     }
    157 
    158     public static PathKeyframes ofPath(Path path, float error) {
    159         return new PathKeyframes(path, error);
    160     }
    161 
    162     /**
    163      * Sets the TypeEvaluator to be used when calculating animated values. This object
    164      * is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet,
    165      * both of which assume their own evaluator to speed up calculations with those primitive
    166      * types.
    167      *
    168      * @param evaluator The TypeEvaluator to be used to calculate animated values.
    169      */
    170     public void setEvaluator(TypeEvaluator evaluator) {
    171         mEvaluator = evaluator;
    172     }
    173 
    174     @Override
    175     public Class getType() {
    176         return mFirstKeyframe.getType();
    177     }
    178 
    179     @Override
    180     public KeyframeSet clone() {
    181         List<Keyframe> keyframes = mKeyframes;
    182         int numKeyframes = mKeyframes.size();
    183         final Keyframe[] newKeyframes = new Keyframe[numKeyframes];
    184         for (int i = 0; i < numKeyframes; ++i) {
    185             newKeyframes[i] = keyframes.get(i).clone();
    186         }
    187         KeyframeSet newSet = new KeyframeSet(newKeyframes);
    188         return newSet;
    189     }
    190 
    191     /**
    192      * Gets the animated value, given the elapsed fraction of the animation (interpolated by the
    193      * animation's interpolator) and the evaluator used to calculate in-between values. This
    194      * function maps the input fraction to the appropriate keyframe interval and a fraction
    195      * between them and returns the interpolated value. Note that the input fraction may fall
    196      * outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
    197      * spring interpolation that might send the fraction past 1.0). We handle this situation by
    198      * just using the two keyframes at the appropriate end when the value is outside those bounds.
    199      *
    200      * @param fraction The elapsed fraction of the animation
    201      * @return The animated value.
    202      */
    203     public Object getValue(float fraction) {
    204         // Special-case optimization for the common case of only two keyframes
    205         if (mNumKeyframes == 2) {
    206             if (mInterpolator != null) {
    207                 fraction = mInterpolator.getInterpolation(fraction);
    208             }
    209             return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
    210                     mLastKeyframe.getValue());
    211         }
    212         if (fraction <= 0f) {
    213             final Keyframe nextKeyframe = mKeyframes.get(1);
    214             final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
    215             if (interpolator != null) {
    216                 fraction = interpolator.getInterpolation(fraction);
    217             }
    218             final float prevFraction = mFirstKeyframe.getFraction();
    219             float intervalFraction = (fraction - prevFraction) /
    220                 (nextKeyframe.getFraction() - prevFraction);
    221             return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
    222                     nextKeyframe.getValue());
    223         } else if (fraction >= 1f) {
    224             final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
    225             final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
    226             if (interpolator != null) {
    227                 fraction = interpolator.getInterpolation(fraction);
    228             }
    229             final float prevFraction = prevKeyframe.getFraction();
    230             float intervalFraction = (fraction - prevFraction) /
    231                 (mLastKeyframe.getFraction() - prevFraction);
    232             return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
    233                     mLastKeyframe.getValue());
    234         }
    235         Keyframe prevKeyframe = mFirstKeyframe;
    236         for (int i = 1; i < mNumKeyframes; ++i) {
    237             Keyframe nextKeyframe = mKeyframes.get(i);
    238             if (fraction < nextKeyframe.getFraction()) {
    239                 final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
    240                 final float prevFraction = prevKeyframe.getFraction();
    241                 float intervalFraction = (fraction - prevFraction) /
    242                     (nextKeyframe.getFraction() - prevFraction);
    243                 // Apply interpolator on the proportional duration.
    244                 if (interpolator != null) {
    245                     intervalFraction = interpolator.getInterpolation(intervalFraction);
    246                 }
    247                 return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
    248                         nextKeyframe.getValue());
    249             }
    250             prevKeyframe = nextKeyframe;
    251         }
    252         // shouldn't reach here
    253         return mLastKeyframe.getValue();
    254     }
    255 
    256     @Override
    257     public String toString() {
    258         String returnVal = " ";
    259         for (int i = 0; i < mNumKeyframes; ++i) {
    260             returnVal += mKeyframes.get(i).getValue() + "  ";
    261         }
    262         return returnVal;
    263     }
    264 }
    265