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