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