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