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.Arrays; 20 import java.util.List; 21 22 import android.animation.Keyframe.IntKeyframe; 23 import android.animation.Keyframe.FloatKeyframe; 24 import android.animation.Keyframe.ObjectKeyframe; 25 import android.graphics.Path; 26 import android.util.Log; 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 KeyframeSet(Keyframe... keyframes)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 /** 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 */ 58 @Override invalidateCache()59 public void invalidateCache() { 60 } 61 getKeyframes()62 public List<Keyframe> getKeyframes() { 63 return mKeyframes; 64 } 65 ofInt(int... values)66 public static KeyframeSet ofInt(int... values) { 67 int numKeyframes = values.length; 68 IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)]; 69 if (numKeyframes == 1) { 70 keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f); 71 keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]); 72 } else { 73 keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]); 74 for (int i = 1; i < numKeyframes; ++i) { 75 keyframes[i] = 76 (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]); 77 } 78 } 79 return new IntKeyframeSet(keyframes); 80 } 81 ofFloat(float... values)82 public static KeyframeSet ofFloat(float... values) { 83 boolean badValue = false; 84 int numKeyframes = values.length; 85 FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)]; 86 if (numKeyframes == 1) { 87 keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f); 88 keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]); 89 if (Float.isNaN(values[0])) { 90 badValue = true; 91 } 92 } else { 93 keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]); 94 for (int i = 1; i < numKeyframes; ++i) { 95 keyframes[i] = 96 (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]); 97 if (Float.isNaN(values[i])) { 98 badValue = true; 99 } 100 } 101 } 102 if (badValue) { 103 Log.w("Animator", "Bad value (NaN) in float animator"); 104 } 105 return new FloatKeyframeSet(keyframes); 106 } 107 ofKeyframe(Keyframe... keyframes)108 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; 121 } 122 } 123 if (hasFloat && !hasInt && !hasOther) { 124 FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes]; 125 for (int i = 0; i < numKeyframes; ++i) { 126 floatKeyframes[i] = (FloatKeyframe) keyframes[i]; 127 } 128 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); 137 } 138 } 139 ofObject(Object... values)140 public static KeyframeSet ofObject(Object... values) { 141 int numKeyframes = values.length; 142 ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)]; 143 if (numKeyframes == 1) { 144 keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f); 145 keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]); 146 } else { 147 keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]); 148 for (int i = 1; i < numKeyframes; ++i) { 149 keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]); 150 } 151 } 152 return new KeyframeSet(keyframes); 153 } 154 ofPath(Path path)155 public static PathKeyframes ofPath(Path path) { 156 return new PathKeyframes(path); 157 } 158 ofPath(Path path, float error)159 public static PathKeyframes ofPath(Path path, float error) { 160 return new PathKeyframes(path, error); 161 } 162 163 /** 164 * 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 */ setEvaluator(TypeEvaluator evaluator)171 public void setEvaluator(TypeEvaluator evaluator) { 172 mEvaluator = evaluator; 173 } 174 175 @Override getType()176 public Class getType() { 177 return mFirstKeyframe.getType(); 178 } 179 180 @Override clone()181 public KeyframeSet clone() { 182 List<Keyframe> keyframes = mKeyframes; 183 int numKeyframes = mKeyframes.size(); 184 final Keyframe[] newKeyframes = new Keyframe[numKeyframes]; 185 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 /** 193 * 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 202 * @return The animated value. 203 */ getValue(float fraction)204 public Object getValue(float fraction) { 205 // 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 } 210 return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(), 211 mLastKeyframe.getValue()); 212 } 213 if (fraction <= 0f) { 214 final Keyframe nextKeyframe = mKeyframes.get(1); 215 final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); 216 if (interpolator != null) { 217 fraction = interpolator.getInterpolation(fraction); 218 } 219 final float prevFraction = mFirstKeyframe.getFraction(); 220 float intervalFraction = (fraction - prevFraction) / 221 (nextKeyframe.getFraction() - prevFraction); 222 return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(), 223 nextKeyframe.getValue()); 224 } else if (fraction >= 1f) { 225 final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2); 226 final TimeInterpolator interpolator = mLastKeyframe.getInterpolator(); 227 if (interpolator != null) { 228 fraction = interpolator.getInterpolation(fraction); 229 } 230 final float prevFraction = prevKeyframe.getFraction(); 231 float intervalFraction = (fraction - prevFraction) / 232 (mLastKeyframe.getFraction() - prevFraction); 233 return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(), 234 mLastKeyframe.getValue()); 235 } 236 Keyframe prevKeyframe = mFirstKeyframe; 237 for (int i = 1; i < mNumKeyframes; ++i) { 238 Keyframe nextKeyframe = mKeyframes.get(i); 239 if (fraction < nextKeyframe.getFraction()) { 240 final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); 241 final float prevFraction = prevKeyframe.getFraction(); 242 float intervalFraction = (fraction - prevFraction) / 243 (nextKeyframe.getFraction() - prevFraction); 244 // Apply interpolator on the proportional duration. 245 if (interpolator != null) { 246 intervalFraction = interpolator.getInterpolation(intervalFraction); 247 } 248 return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(), 249 nextKeyframe.getValue()); 250 } 251 prevKeyframe = nextKeyframe; 252 } 253 // shouldn't reach here 254 return mLastKeyframe.getValue(); 255 } 256 257 @Override toString()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 } 265 } 266