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