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