blob: 77df1512f17a6e7361eebe2c3956c025730613f4 [file] [log] [blame]
Chet Haase3dd207a2010-07-20 14:00:01 -07001/*
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
Chet Haase2794eb32010-10-12 16:29:28 -070019import java.util.Arrays;
Yigit Boyard422dc32014-09-25 12:23:35 -070020import java.util.List;
21
Chet Haase7c608f22010-10-22 17:54:04 -070022import android.animation.Keyframe.IntKeyframe;
23import android.animation.Keyframe.FloatKeyframe;
24import android.animation.Keyframe.ObjectKeyframe;
George Mount984011f2014-08-21 14:28:01 -070025import android.graphics.Path;
Chet Haase20763162013-03-15 13:48:53 -070026import android.util.Log;
Chet Haase3dd207a2010-07-20 14:00:01 -070027
Chet Haase3dd207a2010-07-20 14:00:01 -070028/**
Chet Haasea18a86b2010-09-07 13:20:00 -070029 * This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
Chet Haase3dd207a2010-07-20 14:00:01 -070030 * 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.
Jorim Jaggi787e9dd2016-03-15 10:52:40 +010032 * @hide
Chet Haase3dd207a2010-07-20 14:00:01 -070033 */
Jorim Jaggi787e9dd2016-03-15 10:52:40 +010034public class KeyframeSet implements Keyframes {
Chet Haase3dd207a2010-07-20 14:00:01 -070035
Chet Haase7c608f22010-10-22 17:54:04 -070036 int mNumKeyframes;
Chet Haase3dd207a2010-07-20 14:00:01 -070037
Chet Haase70d4ba12010-10-06 09:46:45 -070038 Keyframe mFirstKeyframe;
39 Keyframe mLastKeyframe;
40 TimeInterpolator mInterpolator; // only used in the 2-keyframe case
Yigit Boyard422dc32014-09-25 12:23:35 -070041 List<Keyframe> mKeyframes; // only used when there are not 2 keyframes
Chet Haase7c608f22010-10-22 17:54:04 -070042 TypeEvaluator mEvaluator;
43
Chet Haase3dd207a2010-07-20 14:00:01 -070044
45 public KeyframeSet(Keyframe... keyframes) {
Chet Haase70d4ba12010-10-06 09:46:45 -070046 mNumKeyframes = keyframes.length;
Yigit Boyard422dc32014-09-25 12:23:35 -070047 // immutable list
48 mKeyframes = Arrays.asList(keyframes);
49 mFirstKeyframe = keyframes[0];
50 mLastKeyframe = keyframes[mNumKeyframes - 1];
Chet Haase70d4ba12010-10-06 09:46:45 -070051 mInterpolator = mLastKeyframe.getInterpolator();
Chet Haase3dd207a2010-07-20 14:00:01 -070052 }
53
Yigit Boyar8619f482014-07-15 17:28:07 -070054 /**
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 */
George Mount984011f2014-08-21 14:28:01 -070058 @Override
59 public void invalidateCache() {
60 }
61
Yigit Boyard422dc32014-09-25 12:23:35 -070062 public List<Keyframe> getKeyframes() {
George Mount984011f2014-08-21 14:28:01 -070063 return mKeyframes;
Yigit Boyar8619f482014-07-15 17:28:07 -070064 }
65
Chet Haase2794eb32010-10-12 16:29:28 -070066 public static KeyframeSet ofInt(int... values) {
67 int numKeyframes = values.length;
Chet Haase7c608f22010-10-22 17:54:04 -070068 IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
Chet Haase2794eb32010-10-12 16:29:28 -070069 if (numKeyframes == 1) {
Chet Haase7c608f22010-10-22 17:54:04 -070070 keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
71 keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
Chet Haase2794eb32010-10-12 16:29:28 -070072 } else {
Chet Haase7c608f22010-10-22 17:54:04 -070073 keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
Chet Haase2794eb32010-10-12 16:29:28 -070074 for (int i = 1; i < numKeyframes; ++i) {
Chet Haase20763162013-03-15 13:48:53 -070075 keyframes[i] =
76 (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
Chet Haase2794eb32010-10-12 16:29:28 -070077 }
78 }
Chet Haase7c608f22010-10-22 17:54:04 -070079 return new IntKeyframeSet(keyframes);
Chet Haase2794eb32010-10-12 16:29:28 -070080 }
81
82 public static KeyframeSet ofFloat(float... values) {
Chet Haase20763162013-03-15 13:48:53 -070083 boolean badValue = false;
Chet Haase2794eb32010-10-12 16:29:28 -070084 int numKeyframes = values.length;
Chet Haase7c608f22010-10-22 17:54:04 -070085 FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
Chet Haase2794eb32010-10-12 16:29:28 -070086 if (numKeyframes == 1) {
Chet Haase7c608f22010-10-22 17:54:04 -070087 keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
88 keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
Chet Haase20763162013-03-15 13:48:53 -070089 if (Float.isNaN(values[0])) {
90 badValue = true;
91 }
Chet Haase2794eb32010-10-12 16:29:28 -070092 } else {
Chet Haase7c608f22010-10-22 17:54:04 -070093 keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
Chet Haase2794eb32010-10-12 16:29:28 -070094 for (int i = 1; i < numKeyframes; ++i) {
Chet Haase20763162013-03-15 13:48:53 -070095 keyframes[i] =
96 (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
97 if (Float.isNaN(values[i])) {
98 badValue = true;
99 }
Chet Haase2794eb32010-10-12 16:29:28 -0700100 }
101 }
Chet Haase20763162013-03-15 13:48:53 -0700102 if (badValue) {
103 Log.w("Animator", "Bad value (NaN) in float animator");
104 }
Chet Haase7c608f22010-10-22 17:54:04 -0700105 return new FloatKeyframeSet(keyframes);
Chet Haase2794eb32010-10-12 16:29:28 -0700106 }
107
Chet Haase7c608f22010-10-22 17:54:04 -0700108 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;
Chet Haase2794eb32010-10-12 16:29:28 -0700121 }
122 }
Chet Haase7c608f22010-10-22 17:54:04 -0700123 if (hasFloat && !hasInt && !hasOther) {
124 FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes];
125 for (int i = 0; i < numKeyframes; ++i) {
126 floatKeyframes[i] = (FloatKeyframe) keyframes[i];
Chet Haase2794eb32010-10-12 16:29:28 -0700127 }
Chet Haase7c608f22010-10-22 17:54:04 -0700128 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);
Chet Haase2794eb32010-10-12 16:29:28 -0700137 }
Chet Haase2794eb32010-10-12 16:29:28 -0700138 }
139
140 public static KeyframeSet ofObject(Object... values) {
141 int numKeyframes = values.length;
Chet Haase7c608f22010-10-22 17:54:04 -0700142 ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];
Chet Haase2794eb32010-10-12 16:29:28 -0700143 if (numKeyframes == 1) {
Chet Haase7c608f22010-10-22 17:54:04 -0700144 keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
145 keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
Chet Haase2794eb32010-10-12 16:29:28 -0700146 } else {
Chet Haase7c608f22010-10-22 17:54:04 -0700147 keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
Chet Haase2794eb32010-10-12 16:29:28 -0700148 for (int i = 1; i < numKeyframes; ++i) {
Chet Haase7c608f22010-10-22 17:54:04 -0700149 keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
Chet Haase2794eb32010-10-12 16:29:28 -0700150 }
151 }
152 return new KeyframeSet(keyframes);
153 }
154
George Mount984011f2014-08-21 14:28:01 -0700155 public static PathKeyframes ofPath(Path path) {
156 return new PathKeyframes(path);
157 }
158
George Mountfd3c4742014-09-08 13:23:36 -0700159 public static PathKeyframes ofPath(Path path, float error) {
160 return new PathKeyframes(path, error);
161 }
162
Chet Haase3dd207a2010-07-20 14:00:01 -0700163 /**
Chet Haase7c608f22010-10-22 17:54:04 -0700164 * 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
George Mount984011f2014-08-21 14:28:01 -0700176 public Class getType() {
177 return mFirstKeyframe.getType();
178 }
179
180 @Override
Chet Haase7c608f22010-10-22 17:54:04 -0700181 public KeyframeSet clone() {
Yigit Boyard422dc32014-09-25 12:23:35 -0700182 List<Keyframe> keyframes = mKeyframes;
Chet Haase7c608f22010-10-22 17:54:04 -0700183 int numKeyframes = mKeyframes.size();
Yigit Boyard422dc32014-09-25 12:23:35 -0700184 final Keyframe[] newKeyframes = new Keyframe[numKeyframes];
Chet Haase7c608f22010-10-22 17:54:04 -0700185 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 /**
Chet Haase3dd207a2010-07-20 14:00:01 -0700193 * 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
Chet Haase3dd207a2010-07-20 14:00:01 -0700202 * @return The animated value.
203 */
Chet Haase7c608f22010-10-22 17:54:04 -0700204 public Object getValue(float fraction) {
Chet Haase70d4ba12010-10-06 09:46:45 -0700205 // Special-case optimization for the common case of only two keyframes
206 if (mNumKeyframes == 2) {
207 if (mInterpolator != null) {
208 fraction = mInterpolator.getInterpolation(fraction);
209 }
Chet Haase7c608f22010-10-22 17:54:04 -0700210 return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
Chet Haase70d4ba12010-10-06 09:46:45 -0700211 mLastKeyframe.getValue());
212 }
Chet Haase3dd207a2010-07-20 14:00:01 -0700213 if (fraction <= 0f) {
Chet Haase3dd207a2010-07-20 14:00:01 -0700214 final Keyframe nextKeyframe = mKeyframes.get(1);
Chet Haasee0ee2e92010-10-07 09:06:18 -0700215 final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
Chet Haase3dd207a2010-07-20 14:00:01 -0700216 if (interpolator != null) {
217 fraction = interpolator.getInterpolation(fraction);
218 }
Chet Haase70d4ba12010-10-06 09:46:45 -0700219 final float prevFraction = mFirstKeyframe.getFraction();
220 float intervalFraction = (fraction - prevFraction) /
221 (nextKeyframe.getFraction() - prevFraction);
Chet Haase7c608f22010-10-22 17:54:04 -0700222 return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
Chet Haase3dd207a2010-07-20 14:00:01 -0700223 nextKeyframe.getValue());
224 } else if (fraction >= 1f) {
225 final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
Chet Haase70d4ba12010-10-06 09:46:45 -0700226 final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
Chet Haase3dd207a2010-07-20 14:00:01 -0700227 if (interpolator != null) {
228 fraction = interpolator.getInterpolation(fraction);
229 }
Chet Haase70d4ba12010-10-06 09:46:45 -0700230 final float prevFraction = prevKeyframe.getFraction();
231 float intervalFraction = (fraction - prevFraction) /
232 (mLastKeyframe.getFraction() - prevFraction);
Chet Haase7c608f22010-10-22 17:54:04 -0700233 return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
Chet Haase70d4ba12010-10-06 09:46:45 -0700234 mLastKeyframe.getValue());
Chet Haase3dd207a2010-07-20 14:00:01 -0700235 }
Chet Haase70d4ba12010-10-06 09:46:45 -0700236 Keyframe prevKeyframe = mFirstKeyframe;
Chet Haase3dd207a2010-07-20 14:00:01 -0700237 for (int i = 1; i < mNumKeyframes; ++i) {
238 Keyframe nextKeyframe = mKeyframes.get(i);
239 if (fraction < nextKeyframe.getFraction()) {
Chet Haasee0ee2e92010-10-07 09:06:18 -0700240 final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
Chet Haase70d4ba12010-10-06 09:46:45 -0700241 final float prevFraction = prevKeyframe.getFraction();
242 float intervalFraction = (fraction - prevFraction) /
243 (nextKeyframe.getFraction() - prevFraction);
Doris Liu6df99052015-03-30 16:03:59 -0700244 // Apply interpolator on the proportional duration.
245 if (interpolator != null) {
246 intervalFraction = interpolator.getInterpolation(intervalFraction);
247 }
Chet Haase7c608f22010-10-22 17:54:04 -0700248 return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
Chet Haase3dd207a2010-07-20 14:00:01 -0700249 nextKeyframe.getValue());
250 }
251 prevKeyframe = nextKeyframe;
252 }
Chet Haase70d4ba12010-10-06 09:46:45 -0700253 // shouldn't reach here
254 return mLastKeyframe.getValue();
Chet Haase3dd207a2010-07-20 14:00:01 -0700255 }
Chet Haasee9140a72011-02-16 16:23:29 -0800256
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 }
Chet Haase3dd207a2010-07-20 14:00:01 -0700265}