ViewPropertyAnimator.java revision 87f4ae67c86c7044253b3e1bcec6956a8c8bf017
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            }
706        };
707        final int currentLayerType = mView.getLayerType();
708        mPendingCleanupAction = new Runnable() {
709            @Override
710            public void run() {
711                mView.setLayerType(currentLayerType, null);
712            }
713        };
714        if (mAnimatorSetupMap == null) {
715            mAnimatorSetupMap = new HashMap<Animator, Runnable>();
716        }
717        if (mAnimatorCleanupMap == null) {
718            mAnimatorCleanupMap = new HashMap<Animator, Runnable>();
719        }
720
721        return this;
722    }
723
724    /**
725     * Specifies an action to take place when the next animation runs. If there is a
726     * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the
727     * action will run after that startDelay expires, when the actual animation begins.
728     * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate
729     * choreographing ViewPropertyAnimator animations with other animations or actions
730     * in the application.
731     *
732     * @param runnable The action to run when the next animation starts.
733     * @return This object, allowing calls to methods in this class to be chained.
734     */
735    public ViewPropertyAnimator withStartAction(Runnable runnable) {
736        mPendingOnStartAction = runnable;
737        if (runnable != null && mAnimatorOnStartMap == null) {
738            mAnimatorOnStartMap = new HashMap<Animator, Runnable>();
739        }
740        return this;
741    }
742
743    /**
744     * Specifies an action to take place when the next animation ends. The action is only
745     * run if the animation ends normally; if the ViewPropertyAnimator is canceled during
746     * that animation, the runnable will not run.
747     * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate
748     * choreographing ViewPropertyAnimator animations with other animations or actions
749     * in the application.
750     *
751     * <p>For example, the following code animates a view to x=200 and then back to 0:</p>
752     * <pre>
753     *     Runnable endAction = new Runnable() {
754     *         public void run() {
755     *             view.animate().x(0);
756     *         }
757     *     };
758     *     view.animate().x(200).withEndAction(endAction);
759     * </pre>
760     *
761     * @param runnable The action to run when the next animation ends.
762     * @return This object, allowing calls to methods in this class to be chained.
763     */
764    public ViewPropertyAnimator withEndAction(Runnable runnable) {
765        mPendingOnEndAction = runnable;
766        if (runnable != null && mAnimatorOnEndMap == null) {
767            mAnimatorOnEndMap = new HashMap<Animator, Runnable>();
768        }
769        return this;
770    }
771
772    /**
773     * Starts the underlying Animator for a set of properties. We use a single animator that
774     * simply runs from 0 to 1, and then use that fractional value to set each property
775     * value accordingly.
776     */
777    private void startAnimation() {
778        mView.setHasTransientState(true);
779        ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
780        ArrayList<NameValuesHolder> nameValueList =
781                (ArrayList<NameValuesHolder>) mPendingAnimations.clone();
782        mPendingAnimations.clear();
783        int propertyMask = 0;
784        int propertyCount = nameValueList.size();
785        for (int i = 0; i < propertyCount; ++i) {
786            NameValuesHolder nameValuesHolder = nameValueList.get(i);
787            propertyMask |= nameValuesHolder.mNameConstant;
788        }
789        mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
790        if (mPendingSetupAction != null) {
791            mAnimatorSetupMap.put(animator, mPendingSetupAction);
792            mPendingSetupAction = null;
793        }
794        if (mPendingCleanupAction != null) {
795            mAnimatorCleanupMap.put(animator, mPendingCleanupAction);
796            mPendingCleanupAction = null;
797        }
798        if (mPendingOnStartAction != null) {
799            mAnimatorOnStartMap.put(animator, mPendingOnStartAction);
800            mPendingOnStartAction = null;
801        }
802        if (mPendingOnEndAction != null) {
803            mAnimatorOnEndMap.put(animator, mPendingOnEndAction);
804            mPendingOnEndAction = null;
805        }
806        animator.addUpdateListener(mAnimatorEventListener);
807        animator.addListener(mAnimatorEventListener);
808        if (mStartDelaySet) {
809            animator.setStartDelay(mStartDelay);
810        }
811        if (mDurationSet) {
812            animator.setDuration(mDuration);
813        }
814        if (mInterpolatorSet) {
815            animator.setInterpolator(mInterpolator);
816        }
817        animator.start();
818    }
819
820    /**
821     * Utility function, called by the various x(), y(), etc. methods. This stores the
822     * constant name for the property along with the from/delta values that will be used to
823     * calculate and set the property during the animation. This structure is added to the
824     * pending animations, awaiting the eventual start() of the underlying animator. A
825     * Runnable is posted to start the animation, and any pending such Runnable is canceled
826     * (which enables us to end up starting just one animator for all of the properties
827     * specified at one time).
828     *
829     * @param constantName The specifier for the property being animated
830     * @param toValue The value to which the property will animate
831     */
832    private void animateProperty(int constantName, float toValue) {
833        float fromValue = getValue(constantName);
834        float deltaValue = toValue - fromValue;
835        animatePropertyBy(constantName, fromValue, deltaValue);
836    }
837
838    /**
839     * Utility function, called by the various xBy(), yBy(), etc. methods. This method is
840     * just like animateProperty(), except the value is an offset from the property's
841     * current value, instead of an absolute "to" value.
842     *
843     * @param constantName The specifier for the property being animated
844     * @param byValue The amount by which the property will change
845     */
846    private void animatePropertyBy(int constantName, float byValue) {
847        float fromValue = getValue(constantName);
848        animatePropertyBy(constantName, fromValue, byValue);
849    }
850
851    /**
852     * Utility function, called by animateProperty() and animatePropertyBy(), which handles the
853     * details of adding a pending animation and posting the request to start the animation.
854     *
855     * @param constantName The specifier for the property being animated
856     * @param startValue The starting value of the property
857     * @param byValue The amount by which the property will change
858     */
859    private void animatePropertyBy(int constantName, float startValue, float byValue) {
860        // First, cancel any existing animations on this property
861        if (mAnimatorMap.size() > 0) {
862            Animator animatorToCancel = null;
863            Set<Animator> animatorSet = mAnimatorMap.keySet();
864            for (Animator runningAnim : animatorSet) {
865                PropertyBundle bundle = mAnimatorMap.get(runningAnim);
866                if (bundle.cancel(constantName)) {
867                    // property was canceled - cancel the animation if it's now empty
868                    // Note that it's safe to break out here because every new animation
869                    // on a property will cancel a previous animation on that property, so
870                    // there can only ever be one such animation running.
871                    if (bundle.mPropertyMask == NONE) {
872                        // the animation is no longer changing anything - cancel it
873                        animatorToCancel = runningAnim;
874                        break;
875                    }
876                }
877            }
878            if (animatorToCancel != null) {
879                animatorToCancel.cancel();
880            }
881        }
882
883        NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
884        mPendingAnimations.add(nameValuePair);
885        mView.removeCallbacks(mAnimationStarter);
886        mView.postOnAnimation(mAnimationStarter);
887    }
888
889    /**
890     * This method handles setting the property values directly in the View object's fields.
891     * propertyConstant tells it which property should be set, value is the value to set
892     * the property to.
893     *
894     * @param propertyConstant The property to be set
895     * @param value The value to set the property to
896     */
897    private void setValue(int propertyConstant, float value) {
898        final View.TransformationInfo info = mView.mTransformationInfo;
899        final DisplayList displayList = mView.mDisplayList;
900        switch (propertyConstant) {
901            case TRANSLATION_X:
902                info.mTranslationX = value;
903                if (displayList != null) displayList.setTranslationX(value);
904                break;
905            case TRANSLATION_Y:
906                info.mTranslationY = value;
907                if (displayList != null) displayList.setTranslationY(value);
908                break;
909            case ROTATION:
910                info.mRotation = value;
911                if (displayList != null) displayList.setRotation(value);
912                break;
913            case ROTATION_X:
914                info.mRotationX = value;
915                if (displayList != null) displayList.setRotationX(value);
916                break;
917            case ROTATION_Y:
918                info.mRotationY = value;
919                if (displayList != null) displayList.setRotationY(value);
920                break;
921            case SCALE_X:
922                info.mScaleX = value;
923                if (displayList != null) displayList.setScaleX(value);
924                break;
925            case SCALE_Y:
926                info.mScaleY = value;
927                if (displayList != null) displayList.setScaleY(value);
928                break;
929            case X:
930                info.mTranslationX = value - mView.mLeft;
931                if (displayList != null) displayList.setTranslationX(value - mView.mLeft);
932                break;
933            case Y:
934                info.mTranslationY = value - mView.mTop;
935                if (displayList != null) displayList.setTranslationY(value - mView.mTop);
936                break;
937            case ALPHA:
938                info.mAlpha = value;
939                if (displayList != null) displayList.setAlpha(value);
940                break;
941        }
942    }
943
944    /**
945     * This method gets the value of the named property from the View object.
946     *
947     * @param propertyConstant The property whose value should be returned
948     * @return float The value of the named property
949     */
950    private float getValue(int propertyConstant) {
951        final View.TransformationInfo info = mView.mTransformationInfo;
952        switch (propertyConstant) {
953            case TRANSLATION_X:
954                return info.mTranslationX;
955            case TRANSLATION_Y:
956                return info.mTranslationY;
957            case ROTATION:
958                return info.mRotation;
959            case ROTATION_X:
960                return info.mRotationX;
961            case ROTATION_Y:
962                return info.mRotationY;
963            case SCALE_X:
964                return info.mScaleX;
965            case SCALE_Y:
966                return info.mScaleY;
967            case X:
968                return mView.mLeft + info.mTranslationX;
969            case Y:
970                return mView.mTop + info.mTranslationY;
971            case ALPHA:
972                return info.mAlpha;
973        }
974        return 0;
975    }
976
977    /**
978     * Utility class that handles the various Animator events. The only ones we care
979     * about are the end event (which we use to clean up the animator map when an animator
980     * finishes) and the update event (which we use to calculate the current value of each
981     * property and then set it on the view object).
982     */
983    private class AnimatorEventListener
984            implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
985        @Override
986        public void onAnimationStart(Animator animation) {
987            if (mAnimatorSetupMap != null) {
988                Runnable r = mAnimatorSetupMap.get(animation);
989                if (r != null) {
990                    r.run();
991                }
992                mAnimatorSetupMap.remove(animation);
993            }
994            if (mAnimatorOnStartMap != null) {
995                Runnable r = mAnimatorOnStartMap.get(animation);
996                if (r != null) {
997                    r.run();
998                }
999                mAnimatorOnStartMap.remove(animation);
1000            }
1001            if (mListener != null) {
1002                mListener.onAnimationStart(animation);
1003            }
1004        }
1005
1006        @Override
1007        public void onAnimationCancel(Animator animation) {
1008            if (mListener != null) {
1009                mListener.onAnimationCancel(animation);
1010            }
1011            if (mAnimatorOnEndMap != null) {
1012                mAnimatorOnEndMap.remove(animation);
1013            }
1014        }
1015
1016        @Override
1017        public void onAnimationRepeat(Animator animation) {
1018            if (mListener != null) {
1019                mListener.onAnimationRepeat(animation);
1020            }
1021        }
1022
1023        @Override
1024        public void onAnimationEnd(Animator animation) {
1025            mView.setHasTransientState(false);
1026            if (mListener != null) {
1027                mListener.onAnimationEnd(animation);
1028            }
1029            if (mAnimatorOnEndMap != null) {
1030                Runnable r = mAnimatorOnEndMap.get(animation);
1031                if (r != null) {
1032                    r.run();
1033                }
1034                mAnimatorOnEndMap.remove(animation);
1035            }
1036            if (mAnimatorCleanupMap != null) {
1037                Runnable r = mAnimatorCleanupMap.get(animation);
1038                if (r != null) {
1039                    r.run();
1040                }
1041                mAnimatorCleanupMap.remove(animation);
1042            }
1043            mAnimatorMap.remove(animation);
1044        }
1045
1046        /**
1047         * Calculate the current value for each property and set it on the view. Invalidate
1048         * the view object appropriately, depending on which properties are being animated.
1049         *
1050         * @param animation The animator associated with the properties that need to be
1051         * set. This animator holds the animation fraction which we will use to calculate
1052         * the current value of each property.
1053         */
1054        @Override
1055        public void onAnimationUpdate(ValueAnimator animation) {
1056            PropertyBundle propertyBundle = mAnimatorMap.get(animation);
1057            if (propertyBundle == null) {
1058                // Shouldn't happen, but just to play it safe
1059                return;
1060            }
1061            boolean useDisplayListProperties = mView.mDisplayList != null;
1062
1063            // alpha requires slightly different treatment than the other (transform) properties.
1064            // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
1065            // logic is dependent on how the view handles an internal call to onSetAlpha().
1066            // We track what kinds of properties are set, and how alpha is handled when it is
1067            // set, and perform the invalidation steps appropriately.
1068            boolean alphaHandled = false;
1069            if (!useDisplayListProperties) {
1070                mView.invalidateParentCaches();
1071            }
1072            float fraction = animation.getAnimatedFraction();
1073            int propertyMask = propertyBundle.mPropertyMask;
1074            if ((propertyMask & TRANSFORM_MASK) != 0) {
1075                mView.invalidateViewProperty(false, false);
1076            }
1077            ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
1078            if (valueList != null) {
1079                int count = valueList.size();
1080                for (int i = 0; i < count; ++i) {
1081                    NameValuesHolder values = valueList.get(i);
1082                    float value = values.mFromValue + fraction * values.mDeltaValue;
1083                    if (values.mNameConstant == ALPHA) {
1084                        alphaHandled = mView.setAlphaNoInvalidation(value);
1085                    } else {
1086                        setValue(values.mNameConstant, value);
1087                    }
1088                }
1089            }
1090            if ((propertyMask & TRANSFORM_MASK) != 0) {
1091                mView.mTransformationInfo.mMatrixDirty = true;
1092                if (!useDisplayListProperties) {
1093                    mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
1094                }
1095            }
1096            // invalidate(false) in all cases except if alphaHandled gets set to true
1097            // via the call to setAlphaNoInvalidation(), above
1098            if (alphaHandled) {
1099                mView.invalidate(true);
1100            } else {
1101                mView.invalidateViewProperty(false, false);
1102            }
1103            if (mUpdateListener != null) {
1104                mUpdateListener.onAnimationUpdate(animation);
1105            }
1106        }
1107    }
1108}
1109