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