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