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