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