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