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