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