ViewPropertyAnimator.java revision 5637b7d2396636886688cf67ba1446882cc5fbfb
1/*
2 * Copyright (C) 2011 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.view;
18
19import android.animation.Animator;
20import android.animation.ValueAnimator;
21import android.animation.TimeInterpolator;
22import android.os.Build;
23
24import java.util.ArrayList;
25import java.util.HashMap;
26import java.util.Set;
27
28/**
29 * This class enables automatic and optimized animation of select properties on View objects.
30 * If only one or two properties on a View object are being animated, then using an
31 * {@link android.animation.ObjectAnimator} is fine; the property setters called by ObjectAnimator
32 * are well equipped to do the right thing to set the property and invalidate the view
33 * appropriately. But if several properties are animated simultaneously, or if you just want a
34 * more convenient syntax to animate a specific property, then ViewPropertyAnimator might be
35 * more well-suited to the task.
36 *
37 * <p>This class may provide better performance for several simultaneous animations, because
38 * it will optimize invalidate calls to take place only once for several properties instead of each
39 * animated property independently causing its own invalidation. Also, the syntax of using this
40 * class could be easier to use because the caller need only tell the View object which
41 * property to animate, and the value to animate either to or by, and this class handles the
42 * details of configuring the underlying Animator class and starting it.</p>
43 *
44 * <p>This class is not constructed by the caller, but rather by the View whose properties
45 * it will animate. Calls to {@link android.view.View#animate()} will return a reference
46 * to the appropriate ViewPropertyAnimator object for that View.</p>
47 *
48 */
49public class ViewPropertyAnimator {
50
51    /**
52     * The View whose properties are being animated by this class. This is set at
53     * construction time.
54     */
55    final View mView;
56
57    /**
58     * The duration of the underlying Animator object. By default, we don't set the duration
59     * on the Animator and just use its default duration. If the duration is ever set on this
60     * Animator, then we use the duration that it was set to.
61     */
62    private long mDuration;
63
64    /**
65     * A flag indicating whether the duration has been set on this object. If not, we don't set
66     * the duration on the underlying Animator, but instead just use its default duration.
67     */
68    private boolean mDurationSet = false;
69
70    /**
71     * The startDelay of the underlying Animator object. By default, we don't set the startDelay
72     * on the Animator and just use its default startDelay. If the startDelay is ever set on this
73     * Animator, then we use the startDelay that it was set to.
74     */
75    private long mStartDelay = 0;
76
77    /**
78     * A flag indicating whether the startDelay has been set on this object. If not, we don't set
79     * the startDelay on the underlying Animator, but instead just use its default startDelay.
80     */
81    private boolean mStartDelaySet = false;
82
83    /**
84     * The interpolator of the underlying Animator object. By default, we don't set the interpolator
85     * on the Animator and just use its default interpolator. If the interpolator is ever set on
86     * this Animator, then we use the interpolator that it was set to.
87     */
88    private TimeInterpolator mInterpolator;
89
90    /**
91     * A flag indicating whether the interpolator has been set on this object. If not, we don't set
92     * the interpolator on the underlying Animator, but instead just use its default interpolator.
93     */
94    private boolean mInterpolatorSet = false;
95
96    /**
97     * Listener for the lifecycle events of the underlying ValueAnimator object.
98     */
99    private Animator.AnimatorListener mListener = null;
100
101    /**
102     * Listener for the update events of the underlying ValueAnimator object.
103     */
104    private ValueAnimator.AnimatorUpdateListener mUpdateListener = null;
105
106    /**
107     * A lazily-created ValueAnimator used in order to get some default animator properties
108     * (duration, start delay, interpolator, etc.).
109     */
110    private ValueAnimator mTempValueAnimator;
111
112    /**
113     * A RenderThread-driven backend that may intercept startAnimation
114     */
115    private ViewPropertyAnimatorRT mRTBackend;
116
117    /**
118     * This listener is the mechanism by which the underlying Animator causes changes to the
119     * properties currently being animated, as well as the cleanup after an animation is
120     * complete.
121     */
122    private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener();
123
124    /**
125     * This list holds the properties that have been asked to animate. We allow the caller to
126     * request several animations prior to actually starting the underlying animator. This
127     * enables us to run one single animator to handle several properties in parallel. Each
128     * property is tossed onto the pending list until the animation actually starts (which is
129     * done by posting it onto mView), at which time the pending list is cleared and the properties
130     * on that list are added to the list of properties associated with that animator.
131     */
132    ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>();
133    private Runnable mPendingSetupAction;
134    private Runnable mPendingCleanupAction;
135    private Runnable mPendingOnStartAction;
136    private Runnable mPendingOnEndAction;
137
138    /**
139     * Constants used to associate a property being requested and the mechanism used to set
140     * the property (this class calls directly into View to set the properties in question).
141     */
142    static final int NONE           = 0x0000;
143    static final int TRANSLATION_X  = 0x0001;
144    static final int TRANSLATION_Y  = 0x0002;
145    static final int TRANSLATION_Z  = 0x0004;
146    static final int SCALE_X        = 0x0008;
147    static final int SCALE_Y        = 0x0010;
148    static final int ROTATION       = 0x0020;
149    static final int ROTATION_X     = 0x0040;
150    static final int ROTATION_Y     = 0x0080;
151    static final int X              = 0x0100;
152    static final int Y              = 0x0200;
153    static final int Z              = 0x0400;
154    static final int ALPHA          = 0x0800;
155
156    private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | TRANSLATION_Z |
157            SCALE_X | SCALE_Y | ROTATION | ROTATION_X | ROTATION_Y | X | Y | Z;
158
159    /**
160     * The mechanism by which the user can request several properties that are then animated
161     * together works by posting this Runnable to start the underlying Animator. Every time
162     * a property animation is requested, we cancel any previous postings of the Runnable
163     * and re-post it. This means that we will only ever run the Runnable (and thus start the
164     * underlying animator) after the caller is done setting the properties that should be
165     * animated together.
166     */
167    private Runnable mAnimationStarter = new Runnable() {
168        @Override
169        public void run() {
170            startAnimation();
171        }
172    };
173
174    /**
175     * This class holds information about the overall animation being run on the set of
176     * properties. The mask describes which properties are being animated and the
177     * values holder is the list of all property/value objects.
178     */
179    private static class PropertyBundle {
180        int mPropertyMask;
181        ArrayList<NameValuesHolder> mNameValuesHolder;
182
183        PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) {
184            mPropertyMask = propertyMask;
185            mNameValuesHolder = nameValuesHolder;
186        }
187
188        /**
189         * Removes the given property from being animated as a part of this
190         * PropertyBundle. If the property was a part of this bundle, it returns
191         * true to indicate that it was, in fact, canceled. This is an indication
192         * to the caller that a cancellation actually occurred.
193         *
194         * @param propertyConstant The property whose cancellation is requested.
195         * @return true if the given property is a part of this bundle and if it
196         * has therefore been canceled.
197         */
198        boolean cancel(int propertyConstant) {
199            if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) {
200                int count = mNameValuesHolder.size();
201                for (int i = 0; i < count; ++i) {
202                    NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i);
203                    if (nameValuesHolder.mNameConstant == propertyConstant) {
204                        mNameValuesHolder.remove(i);
205                        mPropertyMask &= ~propertyConstant;
206                        return true;
207                    }
208                }
209            }
210            return false;
211        }
212    }
213
214    /**
215     * This list tracks the list of properties being animated by any particular animator.
216     * In most situations, there would only ever be one animator running at a time. But it is
217     * possible to request some properties to animate together, then while those properties
218     * are animating, to request some other properties to animate together. The way that
219     * works is by having this map associate the group of properties being animated with the
220     * animator handling the animation. On every update event for an Animator, we ask the
221     * map for the associated properties and set them accordingly.
222     */
223    private HashMap<Animator, PropertyBundle> mAnimatorMap =
224            new HashMap<Animator, PropertyBundle>();
225    private HashMap<Animator, Runnable> mAnimatorSetupMap;
226    private HashMap<Animator, Runnable> mAnimatorCleanupMap;
227    private HashMap<Animator, Runnable> mAnimatorOnStartMap;
228    private HashMap<Animator, Runnable> mAnimatorOnEndMap;
229
230    /**
231     * This is the information we need to set each property during the animation.
232     * mNameConstant is used to set the appropriate field in View, and the from/delta
233     * values are used to calculate the animated value for a given animation fraction
234     * during the animation.
235     */
236    static class NameValuesHolder {
237        int mNameConstant;
238        float mFromValue;
239        float mDeltaValue;
240        NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {
241            mNameConstant = nameConstant;
242            mFromValue = fromValue;
243            mDeltaValue = deltaValue;
244        }
245    }
246
247    /**
248     * Constructor, called by View. This is private by design, as the user should only
249     * get a ViewPropertyAnimator by calling View.animate().
250     *
251     * @param view The View associated with this ViewPropertyAnimator
252     */
253    ViewPropertyAnimator(View view) {
254        mView = view;
255        view.ensureTransformationInfo();
256    }
257
258    /**
259     * Sets the duration for the underlying animator that animates the requested properties.
260     * By default, the animator uses the default value for ValueAnimator. Calling this method
261     * will cause the declared value to be used instead.
262     * @param duration The length of ensuing property animations, in milliseconds. The value
263     * cannot be negative.
264     * @return This object, allowing calls to methods in this class to be chained.
265     */
266    public ViewPropertyAnimator setDuration(long duration) {
267        if (duration < 0) {
268            throw new IllegalArgumentException("Animators cannot have negative duration: " +
269                    duration);
270        }
271        mDurationSet = true;
272        mDuration = duration;
273        return this;
274    }
275
276    /**
277     * Returns the current duration of property animations. If the duration was set on this
278     * object, that value is returned. Otherwise, the default value of the underlying Animator
279     * is returned.
280     *
281     * @see #setDuration(long)
282     * @return The duration of animations, in milliseconds.
283     */
284    public long getDuration() {
285        if (mDurationSet) {
286            return mDuration;
287        } else {
288            // Just return the default from ValueAnimator, since that's what we'd get if
289            // the value has not been set otherwise
290            if (mTempValueAnimator == null) {
291                mTempValueAnimator = new ValueAnimator();
292            }
293            return mTempValueAnimator.getDuration();
294        }
295    }
296
297    /**
298     * Returns the current startDelay of property animations. If the startDelay was set on this
299     * object, that value is returned. Otherwise, the default value of the underlying Animator
300     * is returned.
301     *
302     * @see #setStartDelay(long)
303     * @return The startDelay of animations, in milliseconds.
304     */
305    public long getStartDelay() {
306        if (mStartDelaySet) {
307            return mStartDelay;
308        } else {
309            // Just return the default from ValueAnimator (0), since that's what we'd get if
310            // the value has not been set otherwise
311            return 0;
312        }
313    }
314
315    /**
316     * Sets the startDelay for the underlying animator that animates the requested properties.
317     * By default, the animator uses the default value for ValueAnimator. Calling this method
318     * will cause the declared value to be used instead.
319     * @param startDelay The delay of ensuing property animations, in milliseconds. The value
320     * cannot be negative.
321     * @return This object, allowing calls to methods in this class to be chained.
322     */
323    public ViewPropertyAnimator setStartDelay(long startDelay) {
324        if (startDelay < 0) {
325            throw new IllegalArgumentException("Animators cannot have negative start " +
326                "delay: " + startDelay);
327        }
328        mStartDelaySet = true;
329        mStartDelay = startDelay;
330        return this;
331    }
332
333    /**
334     * Sets the interpolator for the underlying animator that animates the requested properties.
335     * By default, the animator uses the default interpolator for ValueAnimator. Calling this method
336     * will cause the declared object to be used instead.
337     *
338     * @param interpolator The TimeInterpolator to be used for ensuing property animations. A value
339     * of <code>null</code> will result in linear interpolation.
340     * @return This object, allowing calls to methods in this class to be chained.
341     */
342    public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) {
343        mInterpolatorSet = true;
344        mInterpolator = interpolator;
345        return this;
346    }
347
348    /**
349     * Returns the timing interpolator that this animation uses.
350     *
351     * @return The timing interpolator for this animation.
352     */
353    public TimeInterpolator getInterpolator() {
354        if (mInterpolatorSet) {
355            return mInterpolator;
356        } else {
357            // Just return the default from ValueAnimator, since that's what we'd get if
358            // the value has not been set otherwise
359            if (mTempValueAnimator == null) {
360                mTempValueAnimator = new ValueAnimator();
361            }
362            return mTempValueAnimator.getInterpolator();
363        }
364    }
365
366    /**
367     * Sets a listener for events in the underlying Animators that run the property
368     * animations.
369     *
370     * @see Animator.AnimatorListener
371     *
372     * @param listener The listener to be called with AnimatorListener events. A value of
373     * <code>null</code> removes any existing listener.
374     * @return This object, allowing calls to methods in this class to be chained.
375     */
376    public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
377        mListener = listener;
378        return this;
379    }
380
381    Animator.AnimatorListener getListener() {
382        return mListener;
383    }
384
385    /**
386     * Sets a listener for update events in the underlying ValueAnimator that runs
387     * the property animations. Note that the underlying animator is animating between
388     * 0 and 1 (these values are then turned into the actual property values internally
389     * by ViewPropertyAnimator). So the animator cannot give information on the current
390     * values of the properties being animated by this ViewPropertyAnimator, although
391     * the view object itself can be queried to get the current values.
392     *
393     * @see android.animation.ValueAnimator.AnimatorUpdateListener
394     *
395     * @param listener The listener to be called with update events. A value of
396     * <code>null</code> removes any existing listener.
397     * @return This object, allowing calls to methods in this class to be chained.
398     */
399    public ViewPropertyAnimator setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) {
400        mUpdateListener = listener;
401        return this;
402    }
403
404    ValueAnimator.AnimatorUpdateListener getUpdateListener() {
405        return mUpdateListener;
406    }
407
408    /**
409     * Starts the currently pending property animations immediately. Calling <code>start()</code>
410     * is optional because all animations start automatically at the next opportunity. However,
411     * if the animations are needed to start immediately and synchronously (not at the time when
412     * the next event is processed by the hierarchy, which is when the animations would begin
413     * otherwise), then this method can be used.
414     */
415    public void start() {
416        mView.removeCallbacks(mAnimationStarter);
417        startAnimation();
418    }
419
420    /**
421     * Cancels all property animations that are currently running or pending.
422     */
423    public void cancel() {
424        if (mAnimatorMap.size() > 0) {
425            HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
426                    (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone();
427            Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
428            for (Animator runningAnim : animatorSet) {
429                runningAnim.cancel();
430            }
431        }
432        mPendingAnimations.clear();
433        mPendingSetupAction = null;
434        mPendingCleanupAction = null;
435        mPendingOnStartAction = null;
436        mPendingOnEndAction = null;
437        mView.removeCallbacks(mAnimationStarter);
438        if (mRTBackend != null) {
439            mRTBackend.cancelAll();
440        }
441    }
442
443    /**
444     * This method will cause the View's <code>x</code> property to be animated to the
445     * specified value. Animations already running on the property will be canceled.
446     *
447     * @param value The value to be animated to.
448     * @see View#setX(float)
449     * @return This object, allowing calls to methods in this class to be chained.
450     */
451    public ViewPropertyAnimator x(float value) {
452        animateProperty(X, value);
453        return this;
454    }
455
456    /**
457     * This method will cause the View's <code>x</code> property to be animated by the
458     * specified value. Animations already running on the property will be canceled.
459     *
460     * @param value The amount to be animated by, as an offset from the current value.
461     * @see View#setX(float)
462     * @return This object, allowing calls to methods in this class to be chained.
463     */
464    public ViewPropertyAnimator xBy(float value) {
465        animatePropertyBy(X, value);
466        return this;
467    }
468
469    /**
470     * This method will cause the View's <code>y</code> property to be animated to the
471     * specified value. Animations already running on the property will be canceled.
472     *
473     * @param value The value to be animated to.
474     * @see View#setY(float)
475     * @return This object, allowing calls to methods in this class to be chained.
476     */
477    public ViewPropertyAnimator y(float value) {
478        animateProperty(Y, value);
479        return this;
480    }
481
482    /**
483     * This method will cause the View's <code>y</code> property to be animated by the
484     * specified value. Animations already running on the property will be canceled.
485     *
486     * @param value The amount to be animated by, as an offset from the current value.
487     * @see View#setY(float)
488     * @return This object, allowing calls to methods in this class to be chained.
489     */
490    public ViewPropertyAnimator yBy(float value) {
491        animatePropertyBy(Y, value);
492        return this;
493    }
494
495    /**
496     * This method will cause the View's <code>z</code> property to be animated to the
497     * specified value. Animations already running on the property will be canceled.
498     *
499     * @param value The value to be animated to.
500     * @see View#setZ(float)
501     * @return This object, allowing calls to methods in this class to be chained.
502     */
503    public ViewPropertyAnimator z(float value) {
504        animateProperty(Z, value);
505        return this;
506    }
507
508    /**
509     * This method will cause the View's <code>z</code> property to be animated by the
510     * specified value. Animations already running on the property will be canceled.
511     *
512     * @param value The amount to be animated by, as an offset from the current value.
513     * @see View#setZ(float)
514     * @return This object, allowing calls to methods in this class to be chained.
515     */
516    public ViewPropertyAnimator zBy(float value) {
517        animatePropertyBy(Z, value);
518        return this;
519    }
520
521    /**
522     * This method will cause the View's <code>rotation</code> property to be animated to the
523     * specified value. Animations already running on the property will be canceled.
524     *
525     * @param value The value to be animated to.
526     * @see View#setRotation(float)
527     * @return This object, allowing calls to methods in this class to be chained.
528     */
529    public ViewPropertyAnimator rotation(float value) {
530        animateProperty(ROTATION, value);
531        return this;
532    }
533
534    /**
535     * This method will cause the View's <code>rotation</code> property to be animated by the
536     * specified value. Animations already running on the property will be canceled.
537     *
538     * @param value The amount to be animated by, as an offset from the current value.
539     * @see View#setRotation(float)
540     * @return This object, allowing calls to methods in this class to be chained.
541     */
542    public ViewPropertyAnimator rotationBy(float value) {
543        animatePropertyBy(ROTATION, value);
544        return this;
545    }
546
547    /**
548     * This method will cause the View's <code>rotationX</code> property to be animated to the
549     * specified value. Animations already running on the property will be canceled.
550     *
551     * @param value The value to be animated to.
552     * @see View#setRotationX(float)
553     * @return This object, allowing calls to methods in this class to be chained.
554     */
555    public ViewPropertyAnimator rotationX(float value) {
556        animateProperty(ROTATION_X, value);
557        return this;
558    }
559
560    /**
561     * This method will cause the View's <code>rotationX</code> property to be animated by the
562     * specified value. Animations already running on the property will be canceled.
563     *
564     * @param value The amount to be animated by, as an offset from the current value.
565     * @see View#setRotationX(float)
566     * @return This object, allowing calls to methods in this class to be chained.
567     */
568    public ViewPropertyAnimator rotationXBy(float value) {
569        animatePropertyBy(ROTATION_X, value);
570        return this;
571    }
572
573    /**
574     * This method will cause the View's <code>rotationY</code> property to be animated to the
575     * specified value. Animations already running on the property will be canceled.
576     *
577     * @param value The value to be animated to.
578     * @see View#setRotationY(float)
579     * @return This object, allowing calls to methods in this class to be chained.
580     */
581    public ViewPropertyAnimator rotationY(float value) {
582        animateProperty(ROTATION_Y, value);
583        return this;
584    }
585
586    /**
587     * This method will cause the View's <code>rotationY</code> property to be animated by the
588     * specified value. Animations already running on the property will be canceled.
589     *
590     * @param value The amount to be animated by, as an offset from the current value.
591     * @see View#setRotationY(float)
592     * @return This object, allowing calls to methods in this class to be chained.
593     */
594    public ViewPropertyAnimator rotationYBy(float value) {
595        animatePropertyBy(ROTATION_Y, value);
596        return this;
597    }
598
599    /**
600     * This method will cause the View's <code>translationX</code> property to be animated to the
601     * specified value. Animations already running on the property will be canceled.
602     *
603     * @param value The value to be animated to.
604     * @see View#setTranslationX(float)
605     * @return This object, allowing calls to methods in this class to be chained.
606     */
607    public ViewPropertyAnimator translationX(float value) {
608        animateProperty(TRANSLATION_X, value);
609        return this;
610    }
611
612    /**
613     * This method will cause the View's <code>translationX</code> property to be animated by the
614     * specified value. Animations already running on the property will be canceled.
615     *
616     * @param value The amount to be animated by, as an offset from the current value.
617     * @see View#setTranslationX(float)
618     * @return This object, allowing calls to methods in this class to be chained.
619     */
620    public ViewPropertyAnimator translationXBy(float value) {
621        animatePropertyBy(TRANSLATION_X, value);
622        return this;
623    }
624
625    /**
626     * This method will cause the View's <code>translationY</code> property to be animated to the
627     * specified value. Animations already running on the property will be canceled.
628     *
629     * @param value The value to be animated to.
630     * @see View#setTranslationY(float)
631     * @return This object, allowing calls to methods in this class to be chained.
632     */
633    public ViewPropertyAnimator translationY(float value) {
634        animateProperty(TRANSLATION_Y, value);
635        return this;
636    }
637
638    /**
639     * This method will cause the View's <code>translationY</code> property to be animated by the
640     * specified value. Animations already running on the property will be canceled.
641     *
642     * @param value The amount to be animated by, as an offset from the current value.
643     * @see View#setTranslationY(float)
644     * @return This object, allowing calls to methods in this class to be chained.
645     */
646    public ViewPropertyAnimator translationYBy(float value) {
647        animatePropertyBy(TRANSLATION_Y, value);
648        return this;
649    }
650
651    /**
652     * This method will cause the View's <code>translationZ</code> property to be animated to the
653     * specified value. Animations already running on the property will be canceled.
654     *
655     * @param value The value to be animated to.
656     * @see View#setTranslationZ(float)
657     * @return This object, allowing calls to methods in this class to be chained.
658     */
659    public ViewPropertyAnimator translationZ(float value) {
660        animateProperty(TRANSLATION_Z, value);
661        return this;
662    }
663
664    /**
665     * This method will cause the View's <code>translationZ</code> property to be animated by the
666     * specified value. Animations already running on the property will be canceled.
667     *
668     * @param value The amount to be animated by, as an offset from the current value.
669     * @see View#setTranslationZ(float)
670     * @return This object, allowing calls to methods in this class to be chained.
671     */
672    public ViewPropertyAnimator translationZBy(float value) {
673        animatePropertyBy(TRANSLATION_Z, value);
674        return this;
675    }
676    /**
677     * This method will cause the View's <code>scaleX</code> property to be animated to the
678     * specified value. Animations already running on the property will be canceled.
679     *
680     * @param value The value to be animated to.
681     * @see View#setScaleX(float)
682     * @return This object, allowing calls to methods in this class to be chained.
683     */
684    public ViewPropertyAnimator scaleX(float value) {
685        animateProperty(SCALE_X, value);
686        return this;
687    }
688
689    /**
690     * This method will cause the View's <code>scaleX</code> property to be animated by the
691     * specified value. Animations already running on the property will be canceled.
692     *
693     * @param value The amount to be animated by, as an offset from the current value.
694     * @see View#setScaleX(float)
695     * @return This object, allowing calls to methods in this class to be chained.
696     */
697    public ViewPropertyAnimator scaleXBy(float value) {
698        animatePropertyBy(SCALE_X, value);
699        return this;
700    }
701
702    /**
703     * This method will cause the View's <code>scaleY</code> property to be animated to the
704     * specified value. Animations already running on the property will be canceled.
705     *
706     * @param value The value to be animated to.
707     * @see View#setScaleY(float)
708     * @return This object, allowing calls to methods in this class to be chained.
709     */
710    public ViewPropertyAnimator scaleY(float value) {
711        animateProperty(SCALE_Y, value);
712        return this;
713    }
714
715    /**
716     * This method will cause the View's <code>scaleY</code> property to be animated by the
717     * specified value. Animations already running on the property will be canceled.
718     *
719     * @param value The amount to be animated by, as an offset from the current value.
720     * @see View#setScaleY(float)
721     * @return This object, allowing calls to methods in this class to be chained.
722     */
723    public ViewPropertyAnimator scaleYBy(float value) {
724        animatePropertyBy(SCALE_Y, value);
725        return this;
726    }
727
728    /**
729     * This method will cause the View's <code>alpha</code> property to be animated to the
730     * specified value. Animations already running on the property will be canceled.
731     *
732     * @param value The value to be animated to.
733     * @see View#setAlpha(float)
734     * @return This object, allowing calls to methods in this class to be chained.
735     */
736    public ViewPropertyAnimator alpha(float value) {
737        animateProperty(ALPHA, value);
738        return this;
739    }
740
741    /**
742     * This method will cause the View's <code>alpha</code> property to be animated by the
743     * specified value. Animations already running on the property will be canceled.
744     *
745     * @param value The amount to be animated by, as an offset from the current value.
746     * @see View#setAlpha(float)
747     * @return This object, allowing calls to methods in this class to be chained.
748     */
749    public ViewPropertyAnimator alphaBy(float value) {
750        animatePropertyBy(ALPHA, value);
751        return this;
752    }
753
754    /**
755     * The View associated with this ViewPropertyAnimator will have its
756     * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to
757     * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation.
758     * As stated in the documentation for {@link View#LAYER_TYPE_HARDWARE},
759     * the actual type of layer used internally depends on the runtime situation of the
760     * view. If the activity and this view are hardware-accelerated, then the layer will be
761     * accelerated as well. If the activity or the view is not accelerated, then the layer will
762     * effectively be the same as {@link View#LAYER_TYPE_SOFTWARE}.
763     *
764     * <p>This state is not persistent, either on the View or on this ViewPropertyAnimator: the
765     * layer type of the View will be restored when the animation ends to what it was when this
766     * method was called, and this setting on ViewPropertyAnimator is only valid for the next
767     * animation. Note that calling this method and then independently setting the layer type of
768     * the View (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will
769     * result in some inconsistency, including having the layer type restored to its pre-withLayer()
770     * value when the animation ends.</p>
771     *
772     * @see View#setLayerType(int, android.graphics.Paint)
773     * @return This object, allowing calls to methods in this class to be chained.
774     */
775    public ViewPropertyAnimator withLayer() {
776         mPendingSetupAction= new Runnable() {
777            @Override
778            public void run() {
779                mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
780                if (mView.isAttachedToWindow()) {
781                    mView.buildLayer();
782                }
783            }
784        };
785        final int currentLayerType = mView.getLayerType();
786        mPendingCleanupAction = new Runnable() {
787            @Override
788            public void run() {
789                mView.setLayerType(currentLayerType, null);
790            }
791        };
792        if (mAnimatorSetupMap == null) {
793            mAnimatorSetupMap = new HashMap<Animator, Runnable>();
794        }
795        if (mAnimatorCleanupMap == null) {
796            mAnimatorCleanupMap = new HashMap<Animator, Runnable>();
797        }
798
799        return this;
800    }
801
802    /**
803     * Specifies an action to take place when the next animation runs. If there is a
804     * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the
805     * action will run after that startDelay expires, when the actual animation begins.
806     * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate
807     * choreographing ViewPropertyAnimator animations with other animations or actions
808     * in the application.
809     *
810     * @param runnable The action to run when the next animation starts.
811     * @return This object, allowing calls to methods in this class to be chained.
812     */
813    public ViewPropertyAnimator withStartAction(Runnable runnable) {
814        mPendingOnStartAction = runnable;
815        if (runnable != null && mAnimatorOnStartMap == null) {
816            mAnimatorOnStartMap = new HashMap<Animator, Runnable>();
817        }
818        return this;
819    }
820
821    /**
822     * Specifies an action to take place when the next animation ends. The action is only
823     * run if the animation ends normally; if the ViewPropertyAnimator is canceled during
824     * that animation, the runnable will not run.
825     * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate
826     * choreographing ViewPropertyAnimator animations with other animations or actions
827     * in the application.
828     *
829     * <p>For example, the following code animates a view to x=200 and then back to 0:</p>
830     * <pre>
831     *     Runnable endAction = new Runnable() {
832     *         public void run() {
833     *             view.animate().x(0);
834     *         }
835     *     };
836     *     view.animate().x(200).withEndAction(endAction);
837     * </pre>
838     *
839     * @param runnable The action to run when the next animation ends.
840     * @return This object, allowing calls to methods in this class to be chained.
841     */
842    public ViewPropertyAnimator withEndAction(Runnable runnable) {
843        mPendingOnEndAction = runnable;
844        if (runnable != null && mAnimatorOnEndMap == null) {
845            mAnimatorOnEndMap = new HashMap<Animator, Runnable>();
846        }
847        return this;
848    }
849
850    boolean hasActions() {
851        return mPendingSetupAction != null
852                || mPendingCleanupAction != null
853                || mPendingOnStartAction != null
854                || mPendingOnEndAction != null;
855    }
856
857    /**
858     * Starts the underlying Animator for a set of properties. We use a single animator that
859     * simply runs from 0 to 1, and then use that fractional value to set each property
860     * value accordingly.
861     */
862    private void startAnimation() {
863        if (mRTBackend != null && mRTBackend.startAnimation(this)) {
864            return;
865        }
866        mView.setHasTransientState(true);
867        ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
868        ArrayList<NameValuesHolder> nameValueList =
869                (ArrayList<NameValuesHolder>) mPendingAnimations.clone();
870        mPendingAnimations.clear();
871        int propertyMask = 0;
872        int propertyCount = nameValueList.size();
873        for (int i = 0; i < propertyCount; ++i) {
874            NameValuesHolder nameValuesHolder = nameValueList.get(i);
875            propertyMask |= nameValuesHolder.mNameConstant;
876        }
877        mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
878        if (mPendingSetupAction != null) {
879            mAnimatorSetupMap.put(animator, mPendingSetupAction);
880            mPendingSetupAction = null;
881        }
882        if (mPendingCleanupAction != null) {
883            mAnimatorCleanupMap.put(animator, mPendingCleanupAction);
884            mPendingCleanupAction = null;
885        }
886        if (mPendingOnStartAction != null) {
887            mAnimatorOnStartMap.put(animator, mPendingOnStartAction);
888            mPendingOnStartAction = null;
889        }
890        if (mPendingOnEndAction != null) {
891            mAnimatorOnEndMap.put(animator, mPendingOnEndAction);
892            mPendingOnEndAction = null;
893        }
894        animator.addUpdateListener(mAnimatorEventListener);
895        animator.addListener(mAnimatorEventListener);
896        if (mStartDelaySet) {
897            animator.setStartDelay(mStartDelay);
898        }
899        if (mDurationSet) {
900            animator.setDuration(mDuration);
901        }
902        if (mInterpolatorSet) {
903            animator.setInterpolator(mInterpolator);
904        }
905        animator.start();
906    }
907
908    /**
909     * Utility function, called by the various x(), y(), etc. methods. This stores the
910     * constant name for the property along with the from/delta values that will be used to
911     * calculate and set the property during the animation. This structure is added to the
912     * pending animations, awaiting the eventual start() of the underlying animator. A
913     * Runnable is posted to start the animation, and any pending such Runnable is canceled
914     * (which enables us to end up starting just one animator for all of the properties
915     * specified at one time).
916     *
917     * @param constantName The specifier for the property being animated
918     * @param toValue The value to which the property will animate
919     */
920    private void animateProperty(int constantName, float toValue) {
921        float fromValue = getValue(constantName);
922        float deltaValue = toValue - fromValue;
923        animatePropertyBy(constantName, fromValue, deltaValue);
924    }
925
926    /**
927     * Utility function, called by the various xBy(), yBy(), etc. methods. This method is
928     * just like animateProperty(), except the value is an offset from the property's
929     * current value, instead of an absolute "to" value.
930     *
931     * @param constantName The specifier for the property being animated
932     * @param byValue The amount by which the property will change
933     */
934    private void animatePropertyBy(int constantName, float byValue) {
935        float fromValue = getValue(constantName);
936        animatePropertyBy(constantName, fromValue, byValue);
937    }
938
939    /**
940     * Utility function, called by animateProperty() and animatePropertyBy(), which handles the
941     * details of adding a pending animation and posting the request to start the animation.
942     *
943     * @param constantName The specifier for the property being animated
944     * @param startValue The starting value of the property
945     * @param byValue The amount by which the property will change
946     */
947    private void animatePropertyBy(int constantName, float startValue, float byValue) {
948        // First, cancel any existing animations on this property
949        if (mAnimatorMap.size() > 0) {
950            Animator animatorToCancel = null;
951            Set<Animator> animatorSet = mAnimatorMap.keySet();
952            for (Animator runningAnim : animatorSet) {
953                PropertyBundle bundle = mAnimatorMap.get(runningAnim);
954                if (bundle.cancel(constantName)) {
955                    // property was canceled - cancel the animation if it's now empty
956                    // Note that it's safe to break out here because every new animation
957                    // on a property will cancel a previous animation on that property, so
958                    // there can only ever be one such animation running.
959                    if (bundle.mPropertyMask == NONE) {
960                        // the animation is no longer changing anything - cancel it
961                        animatorToCancel = runningAnim;
962                        break;
963                    }
964                }
965            }
966            if (animatorToCancel != null) {
967                animatorToCancel.cancel();
968            }
969        }
970
971        NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
972        mPendingAnimations.add(nameValuePair);
973        mView.removeCallbacks(mAnimationStarter);
974        mView.postOnAnimation(mAnimationStarter);
975    }
976
977    /**
978     * This method handles setting the property values directly in the View object's fields.
979     * propertyConstant tells it which property should be set, value is the value to set
980     * the property to.
981     *
982     * @param propertyConstant The property to be set
983     * @param value The value to set the property to
984     */
985    private void setValue(int propertyConstant, float value) {
986        final View.TransformationInfo info = mView.mTransformationInfo;
987        final RenderNode renderNode = mView.mRenderNode;
988        switch (propertyConstant) {
989            case TRANSLATION_X:
990                renderNode.setTranslationX(value);
991                break;
992            case TRANSLATION_Y:
993                renderNode.setTranslationY(value);
994                break;
995            case TRANSLATION_Z:
996                renderNode.setTranslationZ(value);
997                break;
998            case ROTATION:
999                renderNode.setRotation(value);
1000                break;
1001            case ROTATION_X:
1002                renderNode.setRotationX(value);
1003                break;
1004            case ROTATION_Y:
1005                renderNode.setRotationY(value);
1006                break;
1007            case SCALE_X:
1008                renderNode.setScaleX(value);
1009                break;
1010            case SCALE_Y:
1011                renderNode.setScaleY(value);
1012                break;
1013            case X:
1014                renderNode.setTranslationX(value - mView.mLeft);
1015                break;
1016            case Y:
1017                renderNode.setTranslationY(value - mView.mTop);
1018                break;
1019            case Z:
1020                renderNode.setTranslationZ(value - renderNode.getElevation());
1021                break;
1022            case ALPHA:
1023                info.mAlpha = value;
1024                renderNode.setAlpha(value);
1025                break;
1026        }
1027    }
1028
1029    /**
1030     * This method gets the value of the named property from the View object.
1031     *
1032     * @param propertyConstant The property whose value should be returned
1033     * @return float The value of the named property
1034     */
1035    private float getValue(int propertyConstant) {
1036        final RenderNode node = mView.mRenderNode;
1037        switch (propertyConstant) {
1038            case TRANSLATION_X:
1039                return node.getTranslationX();
1040            case TRANSLATION_Y:
1041                return node.getTranslationY();
1042            case TRANSLATION_Z:
1043                return node.getTranslationZ();
1044            case ROTATION:
1045                return node.getRotation();
1046            case ROTATION_X:
1047                return node.getRotationX();
1048            case ROTATION_Y:
1049                return node.getRotationY();
1050            case SCALE_X:
1051                return node.getScaleX();
1052            case SCALE_Y:
1053                return node.getScaleY();
1054            case X:
1055                return mView.mLeft + node.getTranslationX();
1056            case Y:
1057                return mView.mTop + node.getTranslationY();
1058            case Z:
1059                return node.getElevation() + node.getTranslationZ();
1060            case ALPHA:
1061                return mView.mTransformationInfo.mAlpha;
1062        }
1063        return 0;
1064    }
1065
1066    /**
1067     * Utility class that handles the various Animator events. The only ones we care
1068     * about are the end event (which we use to clean up the animator map when an animator
1069     * finishes) and the update event (which we use to calculate the current value of each
1070     * property and then set it on the view object).
1071     */
1072    private class AnimatorEventListener
1073            implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
1074        @Override
1075        public void onAnimationStart(Animator animation) {
1076            if (mAnimatorSetupMap != null) {
1077                Runnable r = mAnimatorSetupMap.get(animation);
1078                if (r != null) {
1079                    r.run();
1080                }
1081                mAnimatorSetupMap.remove(animation);
1082            }
1083            if (mAnimatorOnStartMap != null) {
1084                Runnable r = mAnimatorOnStartMap.get(animation);
1085                if (r != null) {
1086                    r.run();
1087                }
1088                mAnimatorOnStartMap.remove(animation);
1089            }
1090            if (mListener != null) {
1091                mListener.onAnimationStart(animation);
1092            }
1093        }
1094
1095        @Override
1096        public void onAnimationCancel(Animator animation) {
1097            if (mListener != null) {
1098                mListener.onAnimationCancel(animation);
1099            }
1100            if (mAnimatorOnEndMap != null) {
1101                mAnimatorOnEndMap.remove(animation);
1102            }
1103        }
1104
1105        @Override
1106        public void onAnimationRepeat(Animator animation) {
1107            if (mListener != null) {
1108                mListener.onAnimationRepeat(animation);
1109            }
1110        }
1111
1112        @Override
1113        public void onAnimationEnd(Animator animation) {
1114            mView.setHasTransientState(false);
1115            if (mListener != null) {
1116                mListener.onAnimationEnd(animation);
1117            }
1118            if (mAnimatorOnEndMap != null) {
1119                Runnable r = mAnimatorOnEndMap.get(animation);
1120                if (r != null) {
1121                    r.run();
1122                }
1123                mAnimatorOnEndMap.remove(animation);
1124            }
1125            if (mAnimatorCleanupMap != null) {
1126                Runnable r = mAnimatorCleanupMap.get(animation);
1127                if (r != null) {
1128                    r.run();
1129                }
1130                mAnimatorCleanupMap.remove(animation);
1131            }
1132            mAnimatorMap.remove(animation);
1133        }
1134
1135        /**
1136         * Calculate the current value for each property and set it on the view. Invalidate
1137         * the view object appropriately, depending on which properties are being animated.
1138         *
1139         * @param animation The animator associated with the properties that need to be
1140         * set. This animator holds the animation fraction which we will use to calculate
1141         * the current value of each property.
1142         */
1143        @Override
1144        public void onAnimationUpdate(ValueAnimator animation) {
1145            PropertyBundle propertyBundle = mAnimatorMap.get(animation);
1146            if (propertyBundle == null) {
1147                // Shouldn't happen, but just to play it safe
1148                return;
1149            }
1150
1151            boolean hardwareAccelerated = mView.isHardwareAccelerated();
1152
1153            // alpha requires slightly different treatment than the other (transform) properties.
1154            // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
1155            // logic is dependent on how the view handles an internal call to onSetAlpha().
1156            // We track what kinds of properties are set, and how alpha is handled when it is
1157            // set, and perform the invalidation steps appropriately.
1158            boolean alphaHandled = false;
1159            if (!hardwareAccelerated) {
1160                mView.invalidateParentCaches();
1161            }
1162            float fraction = animation.getAnimatedFraction();
1163            int propertyMask = propertyBundle.mPropertyMask;
1164            if ((propertyMask & TRANSFORM_MASK) != 0) {
1165                mView.invalidateViewProperty(hardwareAccelerated, false);
1166            }
1167            ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
1168            if (valueList != null) {
1169                int count = valueList.size();
1170                for (int i = 0; i < count; ++i) {
1171                    NameValuesHolder values = valueList.get(i);
1172                    float value = values.mFromValue + fraction * values.mDeltaValue;
1173                    if (values.mNameConstant == ALPHA) {
1174                        alphaHandled = mView.setAlphaNoInvalidation(value);
1175                    } else {
1176                        setValue(values.mNameConstant, value);
1177                    }
1178                }
1179            }
1180            if ((propertyMask & TRANSFORM_MASK) != 0) {
1181                if (!hardwareAccelerated) {
1182                    mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
1183                }
1184            }
1185            // invalidate(false) in all cases except if alphaHandled gets set to true
1186            // via the call to setAlphaNoInvalidation(), above
1187            if (alphaHandled) {
1188                mView.invalidate(true);
1189            } else {
1190                mView.invalidateViewProperty(false, false);
1191            }
1192            if (mUpdateListener != null) {
1193                mUpdateListener.onAnimationUpdate(animation);
1194            }
1195        }
1196    }
1197}
1198