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