ObjectAnimator.java revision 3c4ce72c4d66d9ee041924259f20381b658c1529
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;
20import android.util.Property;
21
22import java.lang.reflect.Method;
23import java.util.ArrayList;
24
25/**
26 * This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
27 * The constructors of this class take parameters to define the target object that will be animated
28 * as well as the name of the property that will be animated. Appropriate set/get functions
29 * are then determined internally and the animation will call these functions as necessary to
30 * animate the property.
31 *
32 * @see #setPropertyName(String)
33 *
34 */
35public final class ObjectAnimator extends ValueAnimator {
36    private static final boolean DBG = false;
37
38    // The target object on which the property exists, set in the constructor
39    private Object mTarget;
40
41    private String mPropertyName;
42
43    private Property mProperty;
44
45    /**
46     * Sets the name of the property that will be animated. This name is used to derive
47     * a setter function that will be called to set animated values.
48     * For example, a property name of <code>foo</code> will result
49     * in a call to the function <code>setFoo()</code> on the target object. If either
50     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
51     * also be derived and called.
52     *
53     * <p>For best performance of the mechanism that calls the setter function determined by the
54     * name of the property being animated, use <code>float</code> or <code>int</code> typed values,
55     * and make the setter function for those properties have a <code>void</code> return value. This
56     * will cause the code to take an optimized path for these constrained circumstances. Other
57     * property types and return types will work, but will have more overhead in processing
58     * the requests due to normal reflection mechanisms.</p>
59     *
60     * <p>Note that the setter function derived from this property name
61     * must take the same parameter type as the
62     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
63     * the setter function will fail.</p>
64     *
65     * <p>If this ObjectAnimator has been set up to animate several properties together,
66     * using more than one PropertyValuesHolder objects, then setting the propertyName simply
67     * sets the propertyName in the first of those PropertyValuesHolder objects.</p>
68     *
69     * @param propertyName The name of the property being animated. Should not be null.
70     */
71    public void setPropertyName(String propertyName) {
72        // mValues could be null if this is being constructed piecemeal. Just record the
73        // propertyName to be used later when setValues() is called if so.
74        if (mValues != null) {
75            PropertyValuesHolder valuesHolder = mValues[0];
76            String oldName = valuesHolder.getPropertyName();
77            valuesHolder.setPropertyName(propertyName);
78            mValuesMap.remove(oldName);
79            mValuesMap.put(propertyName, valuesHolder);
80        }
81        mPropertyName = propertyName;
82        // New property/values/target should cause re-initialization prior to starting
83        mInitialized = false;
84    }
85
86    /**
87     * Sets the property that will be animated. Property objects will take precedence over
88     * properties specified by the {@link #setPropertyName(String)} method. Animations should
89     * be set up to use one or the other, not both.
90     *
91     * @param property The property being animated. Should not be null.
92     */
93    public void setProperty(Property property) {
94        // mValues could be null if this is being constructed piecemeal. Just record the
95        // propertyName to be used later when setValues() is called if so.
96        if (mValues != null) {
97            PropertyValuesHolder valuesHolder = mValues[0];
98            String oldName = valuesHolder.getPropertyName();
99            valuesHolder.setProperty(property);
100            mValuesMap.remove(oldName);
101            mValuesMap.put(mPropertyName, valuesHolder);
102        }
103        if (mProperty != null) {
104            mPropertyName = property.getName();
105        }
106        mProperty = property;
107        // New property/values/target should cause re-initialization prior to starting
108        mInitialized = false;
109    }
110
111    /**
112     * Gets the name of the property that will be animated. This name will be used to derive
113     * a setter function that will be called to set animated values.
114     * For example, a property name of <code>foo</code> will result
115     * in a call to the function <code>setFoo()</code> on the target object. If either
116     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
117     * also be derived and called.
118     */
119    public String getPropertyName() {
120        return mPropertyName;
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     * Private utility constructor that initializes the target object and name of the
133     * property being animated.
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     * Private utility constructor that initializes the target object and property being animated.
147     *
148     * @param target The object whose property is to be animated.
149     * @param property The property being animated.
150     */
151    private <T> ObjectAnimator(T target, Property<T, ?> property) {
152        mTarget = target;
153        setProperty(property);
154    }
155
156    /**
157     * Constructs and returns an ObjectAnimator that animates between int values. A single
158     * value implies that that value is the one being animated to. Two values imply a starting
159     * and ending values. More than two values imply a starting value, values to animate through
160     * along the way, and an ending value (these values will be distributed evenly across
161     * the duration of the animation).
162     *
163     * @param target The object whose property is to be animated. This object should
164     * have a public method on it called <code>setName()</code>, where <code>name</code> is
165     * the value of the <code>propertyName</code> parameter.
166     * @param propertyName The name of the property being animated.
167     * @param values A set of values that the animation will animate between over time.
168     * @return An ObjectAnimator object that is set up to animate between the given values.
169     */
170    public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
171        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
172        anim.setIntValues(values);
173        return anim;
174    }
175
176    /**
177     * Constructs and returns an ObjectAnimator that animates between int values. A single
178     * value implies that that value is the one being animated to. Two values imply a starting
179     * and ending values. More than two values imply a starting value, values to animate through
180     * along the way, and an ending value (these values will be distributed evenly across
181     * the duration of the animation).
182     *
183     * @param target The object whose property is to be animated.
184     * @param property The property being animated.
185     * @param values A set of values that the animation will animate between over time.
186     * @return An ObjectAnimator object that is set up to animate between the given values.
187     */
188    public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
189        ObjectAnimator anim = new ObjectAnimator(target, property);
190        anim.setIntValues(values);
191        return anim;
192    }
193
194    /**
195     * Constructs and returns an ObjectAnimator that animates between float values. A single
196     * value implies that that value is the one being animated to. Two values imply a starting
197     * and ending values. More than two values imply a starting value, values to animate through
198     * along the way, and an ending value (these values will be distributed evenly across
199     * the duration of the animation).
200     *
201     * @param target The object whose property is to be animated. This object should
202     * have a public method on it called <code>setName()</code>, where <code>name</code> is
203     * the value of the <code>propertyName</code> parameter.
204     * @param propertyName The name of the property being animated.
205     * @param values A set of values that the animation will animate between over time.
206     * @return An ObjectAnimator object that is set up to animate between the given values.
207     */
208    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
209        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
210        anim.setFloatValues(values);
211        return anim;
212    }
213
214    /**
215     * Constructs and returns an ObjectAnimator that animates between float values. A single
216     * value implies that that value is the one being animated to. Two values imply a starting
217     * and ending values. More than two values imply a starting value, values to animate through
218     * along the way, and an ending value (these values will be distributed evenly across
219     * the duration of the animation).
220     *
221     * @param target The object whose property is to be animated.
222     * @param property The property being animated.
223     * @param values A set of values that the animation will animate between over time.
224     * @return An ObjectAnimator object that is set up to animate between the given values.
225     */
226    public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
227            float... values) {
228        ObjectAnimator anim = new ObjectAnimator(target, property);
229        anim.setFloatValues(values);
230        return anim;
231    }
232
233    /**
234     * Constructs and returns an ObjectAnimator that animates between Object values. A single
235     * value implies that that value is the one being animated to. Two values imply a starting
236     * and ending values. More than two values imply a starting value, values to animate through
237     * along the way, and an ending value (these values will be distributed evenly across
238     * the duration of the animation).
239     *
240     * @param target The object whose property is to be animated. This object should
241     * have a public method on it called <code>setName()</code>, where <code>name</code> is
242     * the value of the <code>propertyName</code> parameter.
243     * @param propertyName The name of the property being animated.
244     * @param evaluator A TypeEvaluator that will be called on each animation frame to
245     * provide the necessary interpolation between the Object values to derive the animated
246     * value.
247     * @param values A set of values that the animation will animate between over time.
248     * @return An ObjectAnimator object that is set up to animate between the given values.
249     */
250    public static ObjectAnimator ofObject(Object target, String propertyName,
251            TypeEvaluator evaluator, Object... values) {
252        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
253        anim.setObjectValues(values);
254        anim.setEvaluator(evaluator);
255        return anim;
256    }
257
258    /**
259     * Constructs and returns an ObjectAnimator that animates between Object values. A single
260     * value implies that that value is the one being animated to. Two values imply a starting
261     * and ending values. More than two values imply a starting value, values to animate through
262     * along the way, and an ending value (these values will be distributed evenly across
263     * the duration of the animation).
264     *
265     * @param target The object whose property is to be animated.
266     * @param property The property being animated.
267     * @param evaluator A TypeEvaluator that will be called on each animation frame to
268     * provide the necessary interpolation between the Object values to derive the animated
269     * value.
270     * @param values A set of values that the animation will animate between over time.
271     * @return An ObjectAnimator object that is set up to animate between the given values.
272     */
273    public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property,
274            TypeEvaluator<V> evaluator, V... values) {
275        ObjectAnimator anim = new ObjectAnimator(target, property);
276        anim.setObjectValues(values);
277        anim.setEvaluator(evaluator);
278        return anim;
279    }
280
281    /**
282     * Constructs and returns an ObjectAnimator that animates between the sets of values specified
283     * in <code>PropertyValueHolder</code> objects. This variant should be used when animating
284     * several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
285     * you to associate a set of animation values with a property name.
286     *
287     * @param target The object whose property is to be animated. Depending on how the
288     * PropertyValuesObjects were constructed, the target object should either have the {@link
289     * android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
290     * PropertyValuesHOlder objects were created with property names) the target object should have
291     * public methods on it called <code>setName()</code>, where <code>name</code> is the name of
292     * the property passed in as the <code>propertyName</code> parameter for each of the
293     * PropertyValuesHolder objects.
294     * @param values A set of PropertyValuesHolder objects whose values will be animated between
295     * over time.
296     * @return An ObjectAnimator object that is set up to animate between the given values.
297     */
298    public static ObjectAnimator ofPropertyValuesHolder(Object target,
299            PropertyValuesHolder... values) {
300        ObjectAnimator anim = new ObjectAnimator();
301        anim.mTarget = target;
302        anim.setValues(values);
303        return anim;
304    }
305
306    @Override
307    public void setIntValues(int... values) {
308        if (mValues == null || mValues.length == 0) {
309            // No values yet - this animator is being constructed piecemeal. Init the values with
310            // whatever the current propertyName is
311            if (mProperty != null) {
312                setValues(PropertyValuesHolder.ofInt(mProperty, values));
313            } else {
314                setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
315            }
316        } else {
317            super.setIntValues(values);
318        }
319    }
320
321    @Override
322    public void setFloatValues(float... values) {
323        if (mValues == null || mValues.length == 0) {
324            // No values yet - this animator is being constructed piecemeal. Init the values with
325            // whatever the current propertyName is
326            if (mProperty != null) {
327                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
328            } else {
329                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
330            }
331        } else {
332            super.setFloatValues(values);
333        }
334    }
335
336    @Override
337    public void setObjectValues(Object... values) {
338        if (mValues == null || mValues.length == 0) {
339            // No values yet - this animator is being constructed piecemeal. Init the values with
340            // whatever the current propertyName is
341            if (mProperty != null) {
342                setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values));
343            } else {
344                setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
345            }
346        } else {
347            super.setObjectValues(values);
348        }
349    }
350
351    @Override
352    public void start() {
353        if (DBG) {
354            Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
355            for (int i = 0; i < mValues.length; ++i) {
356                PropertyValuesHolder pvh = mValues[i];
357                ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
358                Log.d("ObjectAnimator", "   Values[" + i + "]: " +
359                    pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
360                    keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
361            }
362        }
363        super.start();
364    }
365
366    /**
367     * This function is called immediately before processing the first animation
368     * frame of an animation. If there is a nonzero <code>startDelay</code>, the
369     * function is called after that delay ends.
370     * It takes care of the final initialization steps for the
371     * animation. This includes setting mEvaluator, if the user has not yet
372     * set it up, and the setter/getter methods, if the user did not supply
373     * them.
374     *
375     *  <p>Overriders of this method should call the superclass method to cause
376     *  internal mechanisms to be set up correctly.</p>
377     */
378    @Override
379    void initAnimation() {
380        if (!mInitialized) {
381            // mValueType may change due to setter/getter setup; do this before calling super.init(),
382            // which uses mValueType to set up the default type evaluator.
383            int numValues = mValues.length;
384            for (int i = 0; i < numValues; ++i) {
385                mValues[i].setupSetterAndGetter(mTarget);
386            }
387            super.initAnimation();
388        }
389    }
390
391    /**
392     * Sets the length of the animation. The default duration is 300 milliseconds.
393     *
394     * @param duration The length of the animation, in milliseconds.
395     * @return ObjectAnimator The object called with setDuration(). This return
396     * value makes it easier to compose statements together that construct and then set the
397     * duration, as in
398     * <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>.
399     */
400    @Override
401    public ObjectAnimator setDuration(long duration) {
402        super.setDuration(duration);
403        return this;
404    }
405
406
407    /**
408     * The target object whose property will be animated by this animation
409     *
410     * @return The object being animated
411     */
412    public Object getTarget() {
413        return mTarget;
414    }
415
416    /**
417     * Sets the target object whose property will be animated by this animation
418     *
419     * @param target The object being animated
420     */
421    @Override
422    public void setTarget(Object target) {
423        if (mTarget != target) {
424            final Object oldTarget = mTarget;
425            mTarget = target;
426            if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) {
427                return;
428            }
429            // New target type should cause re-initialization prior to starting
430            mInitialized = false;
431        }
432    }
433
434    @Override
435    public void setupStartValues() {
436        initAnimation();
437        int numValues = mValues.length;
438        for (int i = 0; i < numValues; ++i) {
439            mValues[i].setupStartValue(mTarget);
440        }
441    }
442
443    @Override
444    public void setupEndValues() {
445        initAnimation();
446        int numValues = mValues.length;
447        for (int i = 0; i < numValues; ++i) {
448            mValues[i].setupEndValue(mTarget);
449        }
450    }
451
452    /**
453     * This method is called with the elapsed fraction of the animation during every
454     * animation frame. This function turns the elapsed fraction into an interpolated fraction
455     * and then into an animated value (from the evaluator. The function is called mostly during
456     * animation updates, but it is also called when the <code>end()</code>
457     * function is called, to set the final value on the property.
458     *
459     * <p>Overrides of this method must call the superclass to perform the calculation
460     * of the animated value.</p>
461     *
462     * @param fraction The elapsed fraction of the animation.
463     */
464    @Override
465    void animateValue(float fraction) {
466        super.animateValue(fraction);
467        int numValues = mValues.length;
468        for (int i = 0; i < numValues; ++i) {
469            mValues[i].setAnimatedValue(mTarget);
470        }
471    }
472
473    @Override
474    public ObjectAnimator clone() {
475        final ObjectAnimator anim = (ObjectAnimator) super.clone();
476        return anim;
477    }
478
479    @Override
480    public String toString() {
481        String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " +
482            mTarget;
483        if (mValues != null) {
484            for (int i = 0; i < mValues.length; ++i) {
485                returnVal += "\n    " + mValues[i].toString();
486            }
487        }
488        return returnVal;
489    }
490}
491