KeyframeSet.java revision e9140a72b1059574046a624b471b2c3a35806496
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
17package android.animation;
18
19import java.util.ArrayList;
20import java.util.Arrays;
21import android.animation.Keyframe.IntKeyframe;
22import android.animation.Keyframe.FloatKeyframe;
23import 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 */
30class 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