ObjectAnimator.java revision 6e0ecb4eed5cd2e1f15766d7028467129974a12d
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 17package android.animation; 18 19import android.util.Log; 20 21import java.lang.reflect.Method; 22 23/** 24 * This subclass of {@link ValueAnimator} provides support for animating properties on target objects. 25 * The constructors of this class take parameters to define the target object that will be animated 26 * as well as the name of the property that will be animated. Appropriate set/get functions 27 * are then determined internally and the animation will call these functions as necessary to 28 * animate the property. 29 * 30 * @see #setPropertyName(String) 31 * 32 */ 33public final class ObjectAnimator extends ValueAnimator { 34 35 // The target object on which the property exists, set in the constructor 36 private Object mTarget; 37 38 private String mPropertyName; 39 40 /** 41 * Sets the name of the property that will be animated. This name is used to derive 42 * a setter function that will be called to set animated values. 43 * For example, a property name of <code>foo</code> will result 44 * in a call to the function <code>setFoo()</code> on the target object. If either 45 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 46 * also be derived and called. 47 * 48 * <p>For best performance of the mechanism that calls the setter function determined by the 49 * name of the property being animated, use <code>float</code> or <code>int</code> typed values, 50 * and make the setter function for those properties have a <code>void</code> return value. This 51 * will cause the code to take an optimized path for these constrained circumstances. Other 52 * property types and return types will work, but will have more overhead in processing 53 * the requests due to normal reflection mechanisms.</p> 54 * 55 * <p>Note that the setter function derived from this property name 56 * must take the same parameter type as the 57 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 58 * the setter function will fail.</p> 59 * 60 * <p>If this ObjectAnimator has been set up to animate several properties together, 61 * using more than one PropertyValuesHolder objects, then setting the propertyName simply 62 * sets the propertyName in the first of those PropertyValuesHolder objects.</p> 63 * 64 * @param propertyName The name of the property being animated. 65 */ 66 public void setPropertyName(String propertyName) { 67 // mValues could be null if this is being constructed piecemeal. Just record the 68 // propertyName to be used later when setValues() is called if so. 69 if (mValues != null) { 70 PropertyValuesHolder valuesHolder = mValues[0]; 71 String oldName = valuesHolder.getPropertyName(); 72 valuesHolder.setPropertyName(propertyName); 73 mValuesMap.remove(oldName); 74 mValuesMap.put(propertyName, valuesHolder); 75 } 76 mPropertyName = propertyName; 77 // New property/values/target should cause re-initialization prior to starting 78 mInitialized = false; 79 } 80 81 /** 82 * Gets the name of the property that will be animated. This name will be used to derive 83 * a setter function that will be called to set animated values. 84 * For example, a property name of <code>foo</code> will result 85 * in a call to the function <code>setFoo()</code> on the target object. If either 86 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 87 * also be derived and called. 88 */ 89 public String getPropertyName() { 90 return mPropertyName; 91 } 92 93 /** 94 * Determine the setter or getter function using the JavaBeans convention of setFoo or 95 * getFoo for a property named 'foo'. This function figures out what the name of the 96 * function should be and uses reflection to find the Method with that name on the 97 * target object. 98 * 99 * @param prefix "set" or "get", depending on whether we need a setter or getter. 100 * @return Method the method associated with mPropertyName. 101 */ 102 private Method getPropertyFunction(String prefix, Class valueType) { 103 // TODO: faster implementation... 104 Method returnVal = null; 105 String firstLetter = mPropertyName.substring(0, 1); 106 String theRest = mPropertyName.substring(1); 107 firstLetter = firstLetter.toUpperCase(); 108 String setterName = prefix + firstLetter + theRest; 109 Class args[] = null; 110 if (valueType != null) { 111 args = new Class[1]; 112 args[0] = valueType; 113 } 114 try { 115 returnVal = mTarget.getClass().getMethod(setterName, args); 116 } catch (NoSuchMethodException e) { 117 Log.e("ObjectAnimator", 118 "Couldn't find setter/getter for property " + mPropertyName + ": " + e); 119 } 120 return returnVal; 121 } 122 123 /** 124 * Creates a new ObjectAnimator object. This default constructor is primarily for 125 * use internally; the other constructors which take parameters are more generally 126 * useful. 127 */ 128 public ObjectAnimator() { 129 } 130 131 /** 132 * A constructor that takes a single property name and set of values. This constructor is 133 * used in the simple case of animating a single property. 134 * 135 * @param target The object whose property is to be animated. This object should 136 * have a public method on it called <code>setName()</code>, where <code>name</code> is 137 * the value of the <code>propertyName</code> parameter. 138 * @param propertyName The name of the property being animated. 139 */ 140 private ObjectAnimator(Object target, String propertyName) { 141 mTarget = target; 142 setPropertyName(propertyName); 143 } 144 145 /** 146 * Constructs and returns an ObjectAnimator that animates between int values. A single 147 * value implies that that value is the one being animated to. However, this is not typically 148 * useful in a ValueAnimator object because there is no way for the object to determine the 149 * starting value for the animation (unlike ObjectAnimator, which can derive that value 150 * from the target object and property being animated). Therefore, there should typically 151 * be two or more values. 152 * 153 * @param target The object whose property is to be animated. This object should 154 * have a public method on it called <code>setName()</code>, where <code>name</code> is 155 * the value of the <code>propertyName</code> parameter. 156 * @param propertyName The name of the property being animated. 157 * @param values A set of values that the animation will animate between over time. 158 * @return A ValueAnimator object that is set up to animate between the given values. 159 */ 160 public static ObjectAnimator ofInt(Object target, String propertyName, int... values) { 161 ObjectAnimator anim = new ObjectAnimator(target, propertyName); 162 anim.setIntValues(values); 163 return anim; 164 } 165 166 /** 167 * Constructs and returns an ObjectAnimator that animates between float values. A single 168 * value implies that that value is the one being animated to. However, this is not typically 169 * useful in a ValueAnimator object because there is no way for the object to determine the 170 * starting value for the animation (unlike ObjectAnimator, which can derive that value 171 * from the target object and property being animated). Therefore, there should typically 172 * be two or more values. 173 * 174 * @param target The object whose property is to be animated. This object should 175 * have a public method on it called <code>setName()</code>, where <code>name</code> is 176 * the value of the <code>propertyName</code> parameter. 177 * @param propertyName The name of the property being animated. 178 * @param values A set of values that the animation will animate between over time. 179 * @return A ValueAnimator object that is set up to animate between the given values. 180 */ 181 public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { 182 ObjectAnimator anim = new ObjectAnimator(target, propertyName); 183 anim.setFloatValues(values); 184 return anim; 185 } 186 187 /** 188 * A constructor that takes <code>PropertyValueHolder</code> values. This constructor should 189 * be used when animating several properties at once with the same ObjectAnimator, since 190 * PropertyValuesHolder allows you to associate a set of animation values with a property 191 * name. 192 * 193 * @param target The object whose property is to be animated. This object should 194 * have public methods on it called <code>setName()</code>, where <code>name</code> is 195 * the name of the property passed in as the <code>propertyName</code> parameter for 196 * each of the PropertyValuesHolder objects. 197 * @param propertyName The name of the property being animated. 198 * @param evaluator A TypeEvaluator that will be called on each animation frame to 199 * provide the ncessry interpolation between the Object values to derive the animated 200 * value. 201 * @param values The PropertyValuesHolder objects which hold each the property name and values 202 * to animate that property between. 203 */ 204 public static ObjectAnimator ofObject(Object target, String propertyName, 205 TypeEvaluator evaluator, Object... values) { 206 ObjectAnimator anim = new ObjectAnimator(target, propertyName); 207 anim.setObjectValues(values); 208 anim.setEvaluator(evaluator); 209 return anim; 210 } 211 212 /** 213 * Constructs and returns an ObjectAnimator that animates between the sets of values 214 * specifed in <code>PropertyValueHolder</code> objects. This variant should 215 * be used when animating several properties at once with the same ObjectAnimator, since 216 * PropertyValuesHolder allows you to associate a set of animation values with a property 217 * name. 218 * 219 * @param target The object whose property is to be animated. This object should 220 * have public methods on it called <code>setName()</code>, where <code>name</code> is 221 * the name of the property passed in as the <code>propertyName</code> parameter for 222 * each of the PropertyValuesHolder objects. 223 * @param values A set of PropertyValuesHolder objects whose values will be animated 224 * between over time. 225 * @return A ValueAnimator object that is set up to animate between the given values. 226 */ 227 public static ObjectAnimator ofPropertyValuesHolder(Object target, 228 PropertyValuesHolder... values) { 229 ObjectAnimator anim = new ObjectAnimator(); 230 anim.mTarget = target; 231 anim.setValues(values); 232 return anim; 233 } 234 235 @Override 236 public void setIntValues(int... values) { 237 if (mValues == null || mValues.length == 0) { 238 // No values yet - this animator is being constructed piecemeal. Init the values with 239 // whatever the current propertyName is 240 setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); 241 } else { 242 super.setIntValues(values); 243 } 244 } 245 246 @Override 247 public void setFloatValues(float... values) { 248 if (mValues == null || mValues.length == 0) { 249 // No values yet - this animator is being constructed piecemeal. Init the values with 250 // whatever the current propertyName is 251 setValues(PropertyValuesHolder.ofFloat(mPropertyName, values)); 252 } else { 253 super.setFloatValues(values); 254 } 255 } 256 257 @Override 258 public void setObjectValues(Object... values) { 259 if (mValues == null || mValues.length == 0) { 260 // No values yet - this animator is being constructed piecemeal. Init the values with 261 // whatever the current propertyName is 262 setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values)); 263 } else { 264 super.setObjectValues(values); 265 } 266 } 267 268 /** 269 * This function is called immediately before processing the first animation 270 * frame of an animation. If there is a nonzero <code>startDelay</code>, the 271 * function is called after that delay ends. 272 * It takes care of the final initialization steps for the 273 * animation. This includes setting mEvaluator, if the user has not yet 274 * set it up, and the setter/getter methods, if the user did not supply 275 * them. 276 * 277 * <p>Overriders of this method should call the superclass method to cause 278 * internal mechanisms to be set up correctly.</p> 279 */ 280 @Override 281 void initAnimation() { 282 if (!mInitialized) { 283 // mValueType may change due to setter/getter setup; do this before calling super.init(), 284 // which uses mValueType to set up the default type evaluator. 285 int numValues = mValues.length; 286 for (int i = 0; i < numValues; ++i) { 287 mValues[i].setupSetterAndGetter(mTarget); 288 } 289 super.initAnimation(); 290 } 291 } 292 293 /** 294 * Sets the length of the animation. The default duration is 300 milliseconds. 295 * 296 * @param duration The length of the animation, in milliseconds. 297 * @return ObjectAnimator The object called with setDuration(). This return 298 * value makes it easier to compose statements together that construct and then set the 299 * duration, as in 300 * <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>. 301 */ 302 @Override 303 public ObjectAnimator setDuration(long duration) { 304 super.setDuration(duration); 305 return this; 306 } 307 308 309 /** 310 * The target object whose property will be animated by this animation 311 * 312 * @return The object being animated 313 */ 314 public Object getTarget() { 315 return mTarget; 316 } 317 318 /** 319 * Sets the target object whose property will be animated by this animation 320 * 321 * @param target The object being animated 322 */ 323 @Override 324 public void setTarget(Object target) { 325 if (mTarget != target) { 326 mTarget = target; 327 if (mTarget != null && target != null && mTarget.getClass() == target.getClass()) { 328 return; 329 } 330 // New target type should cause re-initialization prior to starting 331 mInitialized = false; 332 } 333 } 334 335 @Override 336 public void setupStartValues() { 337 initAnimation(); 338 int numValues = mValues.length; 339 for (int i = 0; i < numValues; ++i) { 340 mValues[i].setupStartValue(mTarget); 341 } 342 } 343 344 @Override 345 public void setupEndValues() { 346 initAnimation(); 347 int numValues = mValues.length; 348 for (int i = 0; i < numValues; ++i) { 349 mValues[i].setupEndValue(mTarget); 350 } 351 } 352 353 /** 354 * This method is called with the elapsed fraction of the animation during every 355 * animation frame. This function turns the elapsed fraction into an interpolated fraction 356 * and then into an animated value (from the evaluator. The function is called mostly during 357 * animation updates, but it is also called when the <code>end()</code> 358 * function is called, to set the final value on the property. 359 * 360 * <p>Overrides of this method must call the superclass to perform the calculation 361 * of the animated value.</p> 362 * 363 * @param fraction The elapsed fraction of the animation. 364 */ 365 @Override 366 void animateValue(float fraction) { 367 super.animateValue(fraction); 368 int numValues = mValues.length; 369 for (int i = 0; i < numValues; ++i) { 370 mValues[i].setAnimatedValue(mTarget); 371 } 372 } 373 374 @Override 375 public ObjectAnimator clone() { 376 final ObjectAnimator anim = (ObjectAnimator) super.clone(); 377 return anim; 378 } 379} 380