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