ObjectAnimator.java revision 83d6e8213230fb0805aa019d266842253baeb114
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 */ 30public final class ObjectAnimator<T> extends ValueAnimator<T> { 31 32 // The target object on which the property exists, set in the constructor 33 private Object mTarget; 34 35 private String mPropertyName; 36 37 /** 38 * Sets the name of the property that will be animated. This name is used to derive 39 * a setter function that will be called to set animated values. 40 * For example, a property name of <code>foo</code> will result 41 * in a call to the function <code>setFoo()</code> on the target object. If either 42 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 43 * also be derived and called. 44 * 45 * <p>Note that the setter function derived from this property name 46 * must take the same parameter type as the 47 * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to 48 * the setter function will fail.</p> 49 * 50 * <p>If this ObjectAnimator has been set up to animate several properties together, 51 * using more than one PropertyValuesHolder objects, then setting the propertyName simply 52 * sets the propertyName in the first of those PropertyValuesHolder objects.</p> 53 * 54 * @param propertyName The name of the property being animated. 55 */ 56 public void setPropertyName(String propertyName) { 57 // mValues could be null if this is being constructed piecemeal. Just record the 58 // propertyName to be used later when setValues() is called if so. 59 if (mValues != null) { 60 PropertyValuesHolder valuesHolder = mValues[0]; 61 String oldName = valuesHolder.getPropertyName(); 62 valuesHolder.setPropertyName(propertyName); 63 mValuesMap.remove(oldName); 64 mValuesMap.put(propertyName, valuesHolder); 65 } 66 mPropertyName = propertyName; 67 // New property/values/target should cause re-initialization prior to starting 68 mInitialized = false; 69 } 70 71 /** 72 * Gets the name of the property that will be animated. This name will be used to derive 73 * a setter function that will be called to set animated values. 74 * For example, a property name of <code>foo</code> will result 75 * in a call to the function <code>setFoo()</code> on the target object. If either 76 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will 77 * also be derived and called. 78 */ 79 public String getPropertyName() { 80 return mPropertyName; 81 } 82 83 /** 84 * Determine the setter or getter function using the JavaBeans convention of setFoo or 85 * getFoo for a property named 'foo'. This function figures out what the name of the 86 * function should be and uses reflection to find the Method with that name on the 87 * target object. 88 * 89 * @param prefix "set" or "get", depending on whether we need a setter or getter. 90 * @return Method the method associated with mPropertyName. 91 */ 92 private Method getPropertyFunction(String prefix, Class valueType) { 93 // TODO: faster implementation... 94 Method returnVal = null; 95 String firstLetter = mPropertyName.substring(0, 1); 96 String theRest = mPropertyName.substring(1); 97 firstLetter = firstLetter.toUpperCase(); 98 String setterName = prefix + firstLetter + theRest; 99 Class args[] = null; 100 if (valueType != null) { 101 args = new Class[1]; 102 args[0] = valueType; 103 } 104 try { 105 returnVal = mTarget.getClass().getMethod(setterName, args); 106 } catch (NoSuchMethodException e) { 107 Log.e("ObjectAnimator", 108 "Couldn't find setter/getter for property " + mPropertyName + ": " + e); 109 } 110 return returnVal; 111 } 112 113 /** 114 * Creates a new ObjectAnimator object. This default constructor is primarily for 115 * use internally; the other constructors which take parameters are more generally 116 * useful. 117 */ 118 public ObjectAnimator() { 119 } 120 121 /** 122 * A constructor that takes a single property name and set of values. This constructor is 123 * used in the simple case of animating a single property. 124 * 125 * @param duration The length of the animation, in milliseconds. 126 * @param target The object whose property is to be animated. This object should 127 * have a public method on it called <code>setName()</code>, where <code>name</code> is 128 * the value of the <code>propertyName</code> parameter. 129 * @param propertyName The name of the property being animated. 130 * @param values The set of values to animate between. If there is only one value, it 131 * is assumed to be the final value being animated to, and the initial value will be 132 * derived on the fly. 133 */ 134 public ObjectAnimator(long duration, Object target, String propertyName, T...values) { 135 super(duration, (T[]) values); 136 mTarget = target; 137 setPropertyName(propertyName); 138 } 139 140 /** 141 * A constructor that takes <code>PropertyValueHolder</code> values. This constructor should 142 * be used when animating several properties at once with the same ObjectAnimator, since 143 * PropertyValuesHolder allows you to associate a set of animation values with a property 144 * name. 145 * 146 * @param duration The length of the animation, in milliseconds. 147 * @param target The object whose property is to be animated. This object should 148 * have public methods on it called <code>setName()</code>, where <code>name</code> is 149 * the name of the property passed in as the <code>propertyName</code> parameter for 150 * each of the PropertyValuesHolder objects. 151 * @param values The PropertyValuesHolder objects which hold each the property name and values 152 * to animate that property between. 153 */ 154 public ObjectAnimator(long duration, Object target, PropertyValuesHolder...values) { 155 super(duration); 156 setValues(values); 157 mTarget = target; 158 } 159 160 @Override 161 public void setValues(T... values) { 162 if (mValues == null || mValues.length == 0) { 163 // No values yet - this animator is being constructed piecemeal. Init the values with 164 // whatever the current propertyName is 165 setValues(new PropertyValuesHolder[]{ 166 new PropertyValuesHolder(mPropertyName, (Object[])values)}); 167 } else { 168 super.setValues((T[]) values); 169 } 170 } 171 172 /** 173 * This function is called immediately before processing the first animation 174 * frame of an animation. If there is a nonzero <code>startDelay</code>, the 175 * function is called after that delay ends. 176 * It takes care of the final initialization steps for the 177 * animation. This includes setting mEvaluator, if the user has not yet 178 * set it up, and the setter/getter methods, if the user did not supply 179 * them. 180 * 181 * <p>Overriders of this method should call the superclass method to cause 182 * internal mechanisms to be set up correctly.</p> 183 */ 184 @Override 185 void initAnimation() { 186 if (!mInitialized) { 187 // mValueType may change due to setter/getter setup; do this before calling super.init(), 188 // which uses mValueType to set up the default type evaluator. 189 int numValues = mValues.length; 190 for (int i = 0; i < numValues; ++i) { 191 mValues[i].setupSetterAndGetter(mTarget); 192 } 193 super.initAnimation(); 194 } 195 } 196 197 198 /** 199 * The target object whose property will be animated by this animation 200 * 201 * @return The object being animated 202 */ 203 public Object getTarget() { 204 return mTarget; 205 } 206 207 /** 208 * Sets the target object whose property will be animated by this animation 209 * 210 * @param target The object being animated 211 */ 212 @Override 213 public void setTarget(Object target) { 214 mTarget = target; 215 // New property/values/target should cause re-initialization prior to starting 216 mInitialized = false; 217 } 218 219 @Override 220 public void setupStartValues() { 221 initAnimation(); 222 int numValues = mValues.length; 223 for (int i = 0; i < numValues; ++i) { 224 mValues[i].setupStartValue(mTarget); 225 } 226 } 227 228 @Override 229 public void setupEndValues() { 230 initAnimation(); 231 int numValues = mValues.length; 232 for (int i = 0; i < numValues; ++i) { 233 mValues[i].setupEndValue(mTarget); 234 } 235 } 236 237 /** 238 * This method is called with the elapsed fraction of the animation during every 239 * animation frame. This function turns the elapsed fraction into an interpolated fraction 240 * and then into an animated value (from the evaluator. The function is called mostly during 241 * animation updates, but it is also called when the <code>end()</code> 242 * function is called, to set the final value on the property. 243 * 244 * <p>Overrides of this method must call the superclass to perform the calculation 245 * of the animated value.</p> 246 * 247 * @param fraction The elapsed fraction of the animation. 248 */ 249 @Override 250 void animateValue(float fraction) { 251 super.animateValue(fraction); 252 int numValues = mValues.length; 253 for (int i = 0; i < numValues; ++i) { 254 mValues[i].setAnimatedValue(mTarget); 255 } 256 } 257 258 @Override 259 public ObjectAnimator clone() { 260 final ObjectAnimator anim = (ObjectAnimator) super.clone(); 261 return anim; 262 } 263} 264