ViewPropertyAnimator.java revision c1073c3376763a68d4acff68be745227ee63bef9
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    private 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        // TODO: Disabled because of b/15287046
257        //if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
258        //    mRTBackend = new ViewPropertyAnimatorRT(view);
259        //}
260    }
261
262    /**
263     * Sets the duration for the underlying animator that animates the requested properties.
264     * By default, the animator uses the default value for ValueAnimator. Calling this method
265     * will cause the declared value to be used instead.
266     * @param duration The length of ensuing property animations, in milliseconds. The value
267     * cannot be negative.
268     * @return This object, allowing calls to methods in this class to be chained.
269     */
270    public ViewPropertyAnimator setDuration(long duration) {
271        if (duration < 0) {
272            throw new IllegalArgumentException("Animators cannot have negative duration: " +
273                    duration);
274        }
275        mDurationSet = true;
276        mDuration = duration;
277        return this;
278    }
279
280    /**
281     * Returns the current duration of property animations. If the duration was set on this
282     * object, that value is returned. Otherwise, the default value of the underlying Animator
283     * is returned.
284     *
285     * @see #setDuration(long)
286     * @return The duration of animations, in milliseconds.
287     */
288    public long getDuration() {
289        if (mDurationSet) {
290            return mDuration;
291        } else {
292            // Just return the default from ValueAnimator, since that's what we'd get if
293            // the value has not been set otherwise
294            if (mTempValueAnimator == null) {
295                mTempValueAnimator = new ValueAnimator();
296            }
297            return mTempValueAnimator.getDuration();
298        }
299    }
300
301    /**
302     * Returns the current startDelay of property animations. If the startDelay was set on this
303     * object, that value is returned. Otherwise, the default value of the underlying Animator
304     * is returned.
305     *
306     * @see #setStartDelay(long)
307     * @return The startDelay of animations, in milliseconds.
308     */
309    public long getStartDelay() {
310        if (mStartDelaySet) {
311            return mStartDelay;
312        } else {
313            // Just return the default from ValueAnimator (0), since that's what we'd get if
314            // the value has not been set otherwise
315            return 0;
316        }
317    }
318
319    /**
320     * Sets the startDelay for the underlying animator that animates the requested properties.
321     * By default, the animator uses the default value for ValueAnimator. Calling this method
322     * will cause the declared value to be used instead.
323     * @param startDelay The delay of ensuing property animations, in milliseconds. The value
324     * cannot be negative.
325     * @return This object, allowing calls to methods in this class to be chained.
326     */
327    public ViewPropertyAnimator setStartDelay(long startDelay) {
328        if (startDelay < 0) {
329            throw new IllegalArgumentException("Animators cannot have negative duration: " +
330                    startDelay);
331        }
332        mStartDelaySet = true;
333        mStartDelay = startDelay;
334        return this;
335    }
336
337    /**
338     * Sets the interpolator for the underlying animator that animates the requested properties.
339     * By default, the animator uses the default interpolator for ValueAnimator. Calling this method
340     * will cause the declared object to be used instead.
341     *
342     * @param interpolator The TimeInterpolator to be used for ensuing property animations.
343     * @return This object, allowing calls to methods in this class to be chained.
344     */
345    public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) {
346        mInterpolatorSet = true;
347        mInterpolator = interpolator;
348        return this;
349    }
350
351    /**
352     * Returns the timing interpolator that this animation uses.
353     *
354     * @return The timing interpolator for this animation.
355     */
356    public TimeInterpolator getInterpolator() {
357        if (mInterpolatorSet) {
358            return mInterpolator;
359        } else {
360            // Just return the default from ValueAnimator, since that's what we'd get if
361            // the value has not been set otherwise
362            if (mTempValueAnimator == null) {
363                mTempValueAnimator = new ValueAnimator();
364            }
365            return mTempValueAnimator.getInterpolator();
366        }
367    }
368
369    /**
370     * Sets a listener for events in the underlying Animators that run the property
371     * animations.
372     *
373     * @see Animator.AnimatorListener
374     *
375     * @param listener The listener to be called with AnimatorListener events. A value of
376     * <code>null</code> removes any existing listener.
377     * @return This object, allowing calls to methods in this class to be chained.
378     */
379    public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
380        mListener = listener;
381        return this;
382    }
383
384    Animator.AnimatorListener getListener() {
385        return mListener;
386    }
387
388    /**
389     * Sets a listener for update events in the underlying ValueAnimator that runs
390     * the property animations. Note that the underlying animator is animating between
391     * 0 and 1 (these values are then turned into the actual property values internally
392     * by ViewPropertyAnimator). So the animator cannot give information on the current
393     * values of the properties being animated by this ViewPropertyAnimator, although
394     * the view object itself can be queried to get the current values.
395     *
396     * @see android.animation.ValueAnimator.AnimatorUpdateListener
397     *
398     * @param listener The listener to be called with update events. A value of
399     * <code>null</code> removes any existing listener.
400     * @return This object, allowing calls to methods in this class to be chained.
401     */
402    public ViewPropertyAnimator setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) {
403        mUpdateListener = listener;
404        return this;
405    }
406
407    ValueAnimator.AnimatorUpdateListener getUpdateListener() {
408        return mUpdateListener;
409    }
410
411    /**
412     * Starts the currently pending property animations immediately. Calling <code>start()</code>
413     * is optional because all animations start automatically at the next opportunity. However,
414     * if the animations are needed to start immediately and synchronously (not at the time when
415     * the next event is processed by the hierarchy, which is when the animations would begin
416     * otherwise), then this method can be used.
417     */
418    public void start() {
419        mView.removeCallbacks(mAnimationStarter);
420        startAnimation();
421    }
422
423    /**
424     * Cancels all property animations that are currently running or pending.
425     */
426    public void cancel() {
427        if (mAnimatorMap.size() > 0) {
428            HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
429                    (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone();
430            Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
431            for (Animator runningAnim : animatorSet) {
432                runningAnim.cancel();
433            }
434        }
435        mPendingAnimations.clear();
436        mView.removeCallbacks(mAnimationStarter);
437    }
438
439    /**
440     * This method will cause the View's <code>x</code> property to be animated to the
441     * specified value. Animations already running on the property will be canceled.
442     *
443     * @param value The value to be animated to.
444     * @see View#setX(float)
445     * @return This object, allowing calls to methods in this class to be chained.
446     */
447    public ViewPropertyAnimator x(float value) {
448        animateProperty(X, value);
449        return this;
450    }
451
452    /**
453     * This method will cause the View's <code>x</code> property to be animated by the
454     * specified value. Animations already running on the property will be canceled.
455     *
456     * @param value The amount to be animated by, as an offset from the current value.
457     * @see View#setX(float)
458     * @return This object, allowing calls to methods in this class to be chained.
459     */
460    public ViewPropertyAnimator xBy(float value) {
461        animatePropertyBy(X, value);
462        return this;
463    }
464
465    /**
466     * This method will cause the View's <code>y</code> property to be animated to the
467     * specified value. Animations already running on the property will be canceled.
468     *
469     * @param value The value to be animated to.
470     * @see View#setY(float)
471     * @return This object, allowing calls to methods in this class to be chained.
472     */
473    public ViewPropertyAnimator y(float value) {
474        animateProperty(Y, value);
475        return this;
476    }
477
478    /**
479     * This method will cause the View's <code>y</code> property to be animated by the
480     * specified value. Animations already running on the property will be canceled.
481     *
482     * @param value The amount to be animated by, as an offset from the current value.
483     * @see View#setY(float)
484     * @return This object, allowing calls to methods in this class to be chained.
485     */
486    public ViewPropertyAnimator yBy(float value) {
487        animatePropertyBy(Y, value);
488        return this;
489    }
490
491    /**
492     * This method will cause the View's <code>z</code> property to be animated to the
493     * specified value. Animations already running on the property will be canceled.
494     *
495     * @param value The value to be animated to.
496     * @see View#setZ(float)
497     * @return This object, allowing calls to methods in this class to be chained.
498     */
499    public ViewPropertyAnimator z(float value) {
500        animateProperty(Z, value);
501        return this;
502    }
503
504    /**
505     * This method will cause the View's <code>z</code> property to be animated by the
506     * specified value. Animations already running on the property will be canceled.
507     *
508     * @param value The amount to be animated by, as an offset from the current value.
509     * @see View#setZ(float)
510     * @return This object, allowing calls to methods in this class to be chained.
511     */
512    public ViewPropertyAnimator zBy(float value) {
513        animatePropertyBy(Z, value);
514        return this;
515    }
516
517    /**
518     * This method will cause the View's <code>rotation</code> property to be animated to the
519     * specified value. Animations already running on the property will be canceled.
520     *
521     * @param value The value to be animated to.
522     * @see View#setRotation(float)
523     * @return This object, allowing calls to methods in this class to be chained.
524     */
525    public ViewPropertyAnimator rotation(float value) {
526        animateProperty(ROTATION, value);
527        return this;
528    }
529
530    /**
531     * This method will cause the View's <code>rotation</code> property to be animated by the
532     * specified value. Animations already running on the property will be canceled.
533     *
534     * @param value The amount to be animated by, as an offset from the current value.
535     * @see View#setRotation(float)
536     * @return This object, allowing calls to methods in this class to be chained.
537     */
538    public ViewPropertyAnimator rotationBy(float value) {
539        animatePropertyBy(ROTATION, value);
540        return this;
541    }
542
543    /**
544     * This method will cause the View's <code>rotationX</code> property to be animated to the
545     * specified value. Animations already running on the property will be canceled.
546     *
547     * @param value The value to be animated to.
548     * @see View#setRotationX(float)
549     * @return This object, allowing calls to methods in this class to be chained.
550     */
551    public ViewPropertyAnimator rotationX(float value) {
552        animateProperty(ROTATION_X, value);
553        return this;
554    }
555
556    /**
557     * This method will cause the View's <code>rotationX</code> property to be animated by the
558     * specified value. Animations already running on the property will be canceled.
559     *
560     * @param value The amount to be animated by, as an offset from the current value.
561     * @see View#setRotationX(float)
562     * @return This object, allowing calls to methods in this class to be chained.
563     */
564    public ViewPropertyAnimator rotationXBy(float value) {
565        animatePropertyBy(ROTATION_X, value);
566        return this;
567    }
568
569    /**
570     * This method will cause the View's <code>rotationY</code> property to be animated to the
571     * specified value. Animations already running on the property will be canceled.
572     *
573     * @param value The value to be animated to.
574     * @see View#setRotationY(float)
575     * @return This object, allowing calls to methods in this class to be chained.
576     */
577    public ViewPropertyAnimator rotationY(float value) {
578        animateProperty(ROTATION_Y, value);
579        return this;
580    }
581
582    /**
583     * This method will cause the View's <code>rotationY</code> property to be animated by the
584     * specified value. Animations already running on the property will be canceled.
585     *
586     * @param value The amount to be animated by, as an offset from the current value.
587     * @see View#setRotationY(float)
588     * @return This object, allowing calls to methods in this class to be chained.
589     */
590    public ViewPropertyAnimator rotationYBy(float value) {
591        animatePropertyBy(ROTATION_Y, value);
592        return this;
593    }
594
595    /**
596     * This method will cause the View's <code>translationX</code> property to be animated to the
597     * specified value. Animations already running on the property will be canceled.
598     *
599     * @param value The value to be animated to.
600     * @see View#setTranslationX(float)
601     * @return This object, allowing calls to methods in this class to be chained.
602     */
603    public ViewPropertyAnimator translationX(float value) {
604        animateProperty(TRANSLATION_X, value);
605        return this;
606    }
607
608    /**
609     * This method will cause the View's <code>translationX</code> property to be animated by the
610     * specified value. Animations already running on the property will be canceled.
611     *
612     * @param value The amount to be animated by, as an offset from the current value.
613     * @see View#setTranslationX(float)
614     * @return This object, allowing calls to methods in this class to be chained.
615     */
616    public ViewPropertyAnimator translationXBy(float value) {
617        animatePropertyBy(TRANSLATION_X, value);
618        return this;
619    }
620
621    /**
622     * This method will cause the View's <code>translationY</code> property to be animated to the
623     * specified value. Animations already running on the property will be canceled.
624     *
625     * @param value The value to be animated to.
626     * @see View#setTranslationY(float)
627     * @return This object, allowing calls to methods in this class to be chained.
628     */
629    public ViewPropertyAnimator translationY(float value) {
630        animateProperty(TRANSLATION_Y, value);
631        return this;
632    }
633
634    /**
635     * This method will cause the View's <code>translationY</code> property to be animated by the
636     * specified value. Animations already running on the property will be canceled.
637     *
638     * @param value The amount to be animated by, as an offset from the current value.
639     * @see View#setTranslationY(float)
640     * @return This object, allowing calls to methods in this class to be chained.
641     */
642    public ViewPropertyAnimator translationYBy(float value) {
643        animatePropertyBy(TRANSLATION_Y, value);
644        return this;
645    }
646
647    /**
648     * This method will cause the View's <code>translationZ</code> property to be animated to the
649     * specified value. Animations already running on the property will be canceled.
650     *
651     * @param value The value to be animated to.
652     * @see View#setTranslationZ(float)
653     * @return This object, allowing calls to methods in this class to be chained.
654     */
655    public ViewPropertyAnimator translationZ(float value) {
656        animateProperty(TRANSLATION_Z, value);
657        return this;
658    }
659
660    /**
661     * This method will cause the View's <code>translationZ</code> property to be animated by the
662     * specified value. Animations already running on the property will be canceled.
663     *
664     * @param value The amount to be animated by, as an offset from the current value.
665     * @see View#setTranslationZ(float)
666     * @return This object, allowing calls to methods in this class to be chained.
667     */
668    public ViewPropertyAnimator translationZBy(float value) {
669        animatePropertyBy(TRANSLATION_Z, value);
670        return this;
671    }
672    /**
673     * This method will cause the View's <code>scaleX</code> property to be animated to the
674     * specified value. Animations already running on the property will be canceled.
675     *
676     * @param value The value to be animated to.
677     * @see View#setScaleX(float)
678     * @return This object, allowing calls to methods in this class to be chained.
679     */
680    public ViewPropertyAnimator scaleX(float value) {
681        animateProperty(SCALE_X, value);
682        return this;
683    }
684
685    /**
686     * This method will cause the View's <code>scaleX</code> property to be animated by the
687     * specified value. Animations already running on the property will be canceled.
688     *
689     * @param value The amount to be animated by, as an offset from the current value.
690     * @see View#setScaleX(float)
691     * @return This object, allowing calls to methods in this class to be chained.
692     */
693    public ViewPropertyAnimator scaleXBy(float value) {
694        animatePropertyBy(SCALE_X, value);
695        return this;
696    }
697
698    /**
699     * This method will cause the View's <code>scaleY</code> property to be animated to the
700     * specified value. Animations already running on the property will be canceled.
701     *
702     * @param value The value to be animated to.
703     * @see View#setScaleY(float)
704     * @return This object, allowing calls to methods in this class to be chained.
705     */
706    public ViewPropertyAnimator scaleY(float value) {
707        animateProperty(SCALE_Y, value);
708        return this;
709    }
710
711    /**
712     * This method will cause the View's <code>scaleY</code> property to be animated by the
713     * specified value. Animations already running on the property will be canceled.
714     *
715     * @param value The amount to be animated by, as an offset from the current value.
716     * @see View#setScaleY(float)
717     * @return This object, allowing calls to methods in this class to be chained.
718     */
719    public ViewPropertyAnimator scaleYBy(float value) {
720        animatePropertyBy(SCALE_Y, value);
721        return this;
722    }
723
724    /**
725     * This method will cause the View's <code>alpha</code> property to be animated to the
726     * specified value. Animations already running on the property will be canceled.
727     *
728     * @param value The value to be animated to.
729     * @see View#setAlpha(float)
730     * @return This object, allowing calls to methods in this class to be chained.
731     */
732    public ViewPropertyAnimator alpha(float value) {
733        animateProperty(ALPHA, value);
734        return this;
735    }
736
737    /**
738     * This method will cause the View's <code>alpha</code> property to be animated by the
739     * specified value. Animations already running on the property will be canceled.
740     *
741     * @param value The amount to be animated by, as an offset from the current value.
742     * @see View#setAlpha(float)
743     * @return This object, allowing calls to methods in this class to be chained.
744     */
745    public ViewPropertyAnimator alphaBy(float value) {
746        animatePropertyBy(ALPHA, value);
747        return this;
748    }
749
750    /**
751     * The View associated with this ViewPropertyAnimator will have its
752     * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to
753     * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation.
754     * As stated in the documentation for {@link View#LAYER_TYPE_HARDWARE},
755     * the actual type of layer used internally depends on the runtime situation of the
756     * view. If the activity and this view are hardware-accelerated, then the layer will be
757     * accelerated as well. If the activity or the view is not accelerated, then the layer will
758     * effectively be the same as {@link View#LAYER_TYPE_SOFTWARE}.
759     *
760     * <p>This state is not persistent, either on the View or on this ViewPropertyAnimator: the
761     * layer type of the View will be restored when the animation ends to what it was when this
762     * method was called, and this setting on ViewPropertyAnimator is only valid for the next
763     * animation. Note that calling this method and then independently setting the layer type of
764     * the View (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will
765     * result in some inconsistency, including having the layer type restored to its pre-withLayer()
766     * value when the animation ends.</p>
767     *
768     * @see View#setLayerType(int, android.graphics.Paint)
769     * @return This object, allowing calls to methods in this class to be chained.
770     */
771    public ViewPropertyAnimator withLayer() {
772         mPendingSetupAction= new Runnable() {
773            @Override
774            public void run() {
775                mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
776                if (mView.isAttachedToWindow()) {
777                    mView.buildLayer();
778                }
779            }
780        };
781        final int currentLayerType = mView.getLayerType();
782        mPendingCleanupAction = new Runnable() {
783            @Override
784            public void run() {
785                mView.setLayerType(currentLayerType, null);
786            }
787        };
788        if (mAnimatorSetupMap == null) {
789            mAnimatorSetupMap = new HashMap<Animator, Runnable>();
790        }
791        if (mAnimatorCleanupMap == null) {
792            mAnimatorCleanupMap = new HashMap<Animator, Runnable>();
793        }
794
795        return this;
796    }
797
798    /**
799     * Specifies an action to take place when the next animation runs. If there is a
800     * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the
801     * action will run after that startDelay expires, when the actual animation begins.
802     * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate
803     * choreographing ViewPropertyAnimator animations with other animations or actions
804     * in the application.
805     *
806     * @param runnable The action to run when the next animation starts.
807     * @return This object, allowing calls to methods in this class to be chained.
808     */
809    public ViewPropertyAnimator withStartAction(Runnable runnable) {
810        mPendingOnStartAction = runnable;
811        if (runnable != null && mAnimatorOnStartMap == null) {
812            mAnimatorOnStartMap = new HashMap<Animator, Runnable>();
813        }
814        return this;
815    }
816
817    /**
818     * Specifies an action to take place when the next animation ends. The action is only
819     * run if the animation ends normally; if the ViewPropertyAnimator is canceled during
820     * that animation, the runnable will not run.
821     * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate
822     * choreographing ViewPropertyAnimator animations with other animations or actions
823     * in the application.
824     *
825     * <p>For example, the following code animates a view to x=200 and then back to 0:</p>
826     * <pre>
827     *     Runnable endAction = new Runnable() {
828     *         public void run() {
829     *             view.animate().x(0);
830     *         }
831     *     };
832     *     view.animate().x(200).withEndAction(endAction);
833     * </pre>
834     *
835     * @param runnable The action to run when the next animation ends.
836     * @return This object, allowing calls to methods in this class to be chained.
837     */
838    public ViewPropertyAnimator withEndAction(Runnable runnable) {
839        mPendingOnEndAction = runnable;
840        if (runnable != null && mAnimatorOnEndMap == null) {
841            mAnimatorOnEndMap = new HashMap<Animator, Runnable>();
842        }
843        return this;
844    }
845
846    boolean hasActions() {
847        return mPendingSetupAction != null
848                || mPendingCleanupAction != null
849                || mPendingOnStartAction != null
850                || mPendingOnEndAction != null;
851    }
852
853    /**
854     * Starts the underlying Animator for a set of properties. We use a single animator that
855     * simply runs from 0 to 1, and then use that fractional value to set each property
856     * value accordingly.
857     */
858    private void startAnimation() {
859        if (mRTBackend != null && mRTBackend.startAnimation(this)) {
860            return;
861        }
862        mView.setHasTransientState(true);
863        ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
864        ArrayList<NameValuesHolder> nameValueList =
865                (ArrayList<NameValuesHolder>) mPendingAnimations.clone();
866        mPendingAnimations.clear();
867        int propertyMask = 0;
868        int propertyCount = nameValueList.size();
869        for (int i = 0; i < propertyCount; ++i) {
870            NameValuesHolder nameValuesHolder = nameValueList.get(i);
871            propertyMask |= nameValuesHolder.mNameConstant;
872        }
873        mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
874        if (mPendingSetupAction != null) {
875            mAnimatorSetupMap.put(animator, mPendingSetupAction);
876            mPendingSetupAction = null;
877        }
878        if (mPendingCleanupAction != null) {
879            mAnimatorCleanupMap.put(animator, mPendingCleanupAction);
880            mPendingCleanupAction = null;
881        }
882        if (mPendingOnStartAction != null) {
883            mAnimatorOnStartMap.put(animator, mPendingOnStartAction);
884            mPendingOnStartAction = null;
885        }
886        if (mPendingOnEndAction != null) {
887            mAnimatorOnEndMap.put(animator, mPendingOnEndAction);
888            mPendingOnEndAction = null;
889        }
890        animator.addUpdateListener(mAnimatorEventListener);
891        animator.addListener(mAnimatorEventListener);
892        if (mStartDelaySet) {
893            animator.setStartDelay(mStartDelay);
894        }
895        if (mDurationSet) {
896            animator.setDuration(mDuration);
897        }
898        if (mInterpolatorSet) {
899            animator.setInterpolator(mInterpolator);
900        }
901        animator.start();
902    }
903
904    /**
905     * Utility function, called by the various x(), y(), etc. methods. This stores the
906     * constant name for the property along with the from/delta values that will be used to
907     * calculate and set the property during the animation. This structure is added to the
908     * pending animations, awaiting the eventual start() of the underlying animator. A
909     * Runnable is posted to start the animation, and any pending such Runnable is canceled
910     * (which enables us to end up starting just one animator for all of the properties
911     * specified at one time).
912     *
913     * @param constantName The specifier for the property being animated
914     * @param toValue The value to which the property will animate
915     */
916    private void animateProperty(int constantName, float toValue) {
917        float fromValue = getValue(constantName);
918        float deltaValue = toValue - fromValue;
919        animatePropertyBy(constantName, fromValue, deltaValue);
920    }
921
922    /**
923     * Utility function, called by the various xBy(), yBy(), etc. methods. This method is
924     * just like animateProperty(), except the value is an offset from the property's
925     * current value, instead of an absolute "to" value.
926     *
927     * @param constantName The specifier for the property being animated
928     * @param byValue The amount by which the property will change
929     */
930    private void animatePropertyBy(int constantName, float byValue) {
931        float fromValue = getValue(constantName);
932        animatePropertyBy(constantName, fromValue, byValue);
933    }
934
935    /**
936     * Utility function, called by animateProperty() and animatePropertyBy(), which handles the
937     * details of adding a pending animation and posting the request to start the animation.
938     *
939     * @param constantName The specifier for the property being animated
940     * @param startValue The starting value of the property
941     * @param byValue The amount by which the property will change
942     */
943    private void animatePropertyBy(int constantName, float startValue, float byValue) {
944        // First, cancel any existing animations on this property
945        if (mAnimatorMap.size() > 0) {
946            Animator animatorToCancel = null;
947            Set<Animator> animatorSet = mAnimatorMap.keySet();
948            for (Animator runningAnim : animatorSet) {
949                PropertyBundle bundle = mAnimatorMap.get(runningAnim);
950                if (bundle.cancel(constantName)) {
951                    // property was canceled - cancel the animation if it's now empty
952                    // Note that it's safe to break out here because every new animation
953                    // on a property will cancel a previous animation on that property, so
954                    // there can only ever be one such animation running.
955                    if (bundle.mPropertyMask == NONE) {
956                        // the animation is no longer changing anything - cancel it
957                        animatorToCancel = runningAnim;
958                        break;
959                    }
960                }
961            }
962            if (animatorToCancel != null) {
963                animatorToCancel.cancel();
964            }
965        }
966
967        NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
968        mPendingAnimations.add(nameValuePair);
969        mView.removeCallbacks(mAnimationStarter);
970        mView.postOnAnimation(mAnimationStarter);
971    }
972
973    /**
974     * This method handles setting the property values directly in the View object's fields.
975     * propertyConstant tells it which property should be set, value is the value to set
976     * the property to.
977     *
978     * @param propertyConstant The property to be set
979     * @param value The value to set the property to
980     */
981    private void setValue(int propertyConstant, float value) {
982        final View.TransformationInfo info = mView.mTransformationInfo;
983        final RenderNode renderNode = mView.mRenderNode;
984        switch (propertyConstant) {
985            case TRANSLATION_X:
986                renderNode.setTranslationX(value);
987                break;
988            case TRANSLATION_Y:
989                renderNode.setTranslationY(value);
990                break;
991            case TRANSLATION_Z:
992                renderNode.setTranslationZ(value);
993                break;
994            case ROTATION:
995                renderNode.setRotation(value);
996                break;
997            case ROTATION_X:
998                renderNode.setRotationX(value);
999                break;
1000            case ROTATION_Y:
1001                renderNode.setRotationY(value);
1002                break;
1003            case SCALE_X:
1004                renderNode.setScaleX(value);
1005                break;
1006            case SCALE_Y:
1007                renderNode.setScaleY(value);
1008                break;
1009            case X:
1010                renderNode.setTranslationX(value - mView.mLeft);
1011                break;
1012            case Y:
1013                renderNode.setTranslationY(value - mView.mTop);
1014                break;
1015            case Z:
1016                renderNode.setTranslationZ(value - renderNode.getElevation());
1017                break;
1018            case ALPHA:
1019                info.mAlpha = value;
1020                renderNode.setAlpha(value);
1021                break;
1022        }
1023    }
1024
1025    /**
1026     * This method gets the value of the named property from the View object.
1027     *
1028     * @param propertyConstant The property whose value should be returned
1029     * @return float The value of the named property
1030     */
1031    private float getValue(int propertyConstant) {
1032        final RenderNode node = mView.mRenderNode;
1033        switch (propertyConstant) {
1034            case TRANSLATION_X:
1035                return node.getTranslationX();
1036            case TRANSLATION_Y:
1037                return node.getTranslationY();
1038            case TRANSLATION_Z:
1039                return node.getTranslationZ();
1040            case ROTATION:
1041                return node.getRotation();
1042            case ROTATION_X:
1043                return node.getRotationX();
1044            case ROTATION_Y:
1045                return node.getRotationY();
1046            case SCALE_X:
1047                return node.getScaleX();
1048            case SCALE_Y:
1049                return node.getScaleY();
1050            case X:
1051                return mView.mLeft + node.getTranslationX();
1052            case Y:
1053                return mView.mTop + node.getTranslationY();
1054            case Z:
1055                return node.getElevation() + node.getTranslationZ();
1056            case ALPHA:
1057                return mView.mTransformationInfo.mAlpha;
1058        }
1059        return 0;
1060    }
1061
1062    /**
1063     * Utility class that handles the various Animator events. The only ones we care
1064     * about are the end event (which we use to clean up the animator map when an animator
1065     * finishes) and the update event (which we use to calculate the current value of each
1066     * property and then set it on the view object).
1067     */
1068    private class AnimatorEventListener
1069            implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
1070        @Override
1071        public void onAnimationStart(Animator animation) {
1072            if (mAnimatorSetupMap != null) {
1073                Runnable r = mAnimatorSetupMap.get(animation);
1074                if (r != null) {
1075                    r.run();
1076                }
1077                mAnimatorSetupMap.remove(animation);
1078            }
1079            if (mAnimatorOnStartMap != null) {
1080                Runnable r = mAnimatorOnStartMap.get(animation);
1081                if (r != null) {
1082                    r.run();
1083                }
1084                mAnimatorOnStartMap.remove(animation);
1085            }
1086            if (mListener != null) {
1087                mListener.onAnimationStart(animation);
1088            }
1089        }
1090
1091        @Override
1092        public void onAnimationCancel(Animator animation) {
1093            if (mListener != null) {
1094                mListener.onAnimationCancel(animation);
1095            }
1096            if (mAnimatorOnEndMap != null) {
1097                mAnimatorOnEndMap.remove(animation);
1098            }
1099        }
1100
1101        @Override
1102        public void onAnimationRepeat(Animator animation) {
1103            if (mListener != null) {
1104                mListener.onAnimationRepeat(animation);
1105            }
1106        }
1107
1108        @Override
1109        public void onAnimationEnd(Animator animation) {
1110            mView.setHasTransientState(false);
1111            if (mListener != null) {
1112                mListener.onAnimationEnd(animation);
1113            }
1114            if (mAnimatorOnEndMap != null) {
1115                Runnable r = mAnimatorOnEndMap.get(animation);
1116                if (r != null) {
1117                    r.run();
1118                }
1119                mAnimatorOnEndMap.remove(animation);
1120            }
1121            if (mAnimatorCleanupMap != null) {
1122                Runnable r = mAnimatorCleanupMap.get(animation);
1123                if (r != null) {
1124                    r.run();
1125                }
1126                mAnimatorCleanupMap.remove(animation);
1127            }
1128            mAnimatorMap.remove(animation);
1129        }
1130
1131        /**
1132         * Calculate the current value for each property and set it on the view. Invalidate
1133         * the view object appropriately, depending on which properties are being animated.
1134         *
1135         * @param animation The animator associated with the properties that need to be
1136         * set. This animator holds the animation fraction which we will use to calculate
1137         * the current value of each property.
1138         */
1139        @Override
1140        public void onAnimationUpdate(ValueAnimator animation) {
1141            PropertyBundle propertyBundle = mAnimatorMap.get(animation);
1142            if (propertyBundle == null) {
1143                // Shouldn't happen, but just to play it safe
1144                return;
1145            }
1146
1147            boolean hardwareAccelerated = mView.isHardwareAccelerated();
1148
1149            // alpha requires slightly different treatment than the other (transform) properties.
1150            // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
1151            // logic is dependent on how the view handles an internal call to onSetAlpha().
1152            // We track what kinds of properties are set, and how alpha is handled when it is
1153            // set, and perform the invalidation steps appropriately.
1154            boolean alphaHandled = false;
1155            if (!hardwareAccelerated) {
1156                mView.invalidateParentCaches();
1157            }
1158            float fraction = animation.getAnimatedFraction();
1159            int propertyMask = propertyBundle.mPropertyMask;
1160            if ((propertyMask & TRANSFORM_MASK) != 0) {
1161                mView.invalidateViewProperty(hardwareAccelerated, false);
1162            }
1163            ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
1164            if (valueList != null) {
1165                int count = valueList.size();
1166                for (int i = 0; i < count; ++i) {
1167                    NameValuesHolder values = valueList.get(i);
1168                    float value = values.mFromValue + fraction * values.mDeltaValue;
1169                    if (values.mNameConstant == ALPHA) {
1170                        alphaHandled = mView.setAlphaNoInvalidation(value);
1171                    } else {
1172                        setValue(values.mNameConstant, value);
1173                    }
1174                }
1175            }
1176            if ((propertyMask & TRANSFORM_MASK) != 0) {
1177                if (!hardwareAccelerated) {
1178                    mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
1179                }
1180            }
1181            // invalidate(false) in all cases except if alphaHandled gets set to true
1182            // via the call to setAlphaNoInvalidation(), above
1183            if (alphaHandled) {
1184                mView.invalidate(true);
1185            } else {
1186                mView.invalidateViewProperty(false, false);
1187            }
1188            if (mUpdateListener != null) {
1189                mUpdateListener.onAnimationUpdate(animation);
1190            }
1191        }
1192    }
1193}
1194