• 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.ValueAnimator;
21 import android.animation.TimeInterpolator;
22 
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import 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  */
48 public 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
97      */
98     private Animator.AnimatorListener mListener = null;
99 
100     /**
101      * This listener is the mechanism by which the underlying Animator causes changes to the
102      * properties currently being animated, as well as the cleanup after an animation is
103      * complete.
104      */
105     private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener();
106 
107     /**
108      * This list holds the properties that have been asked to animate. We allow the caller to
109      * request several animations prior to actually starting the underlying animator. This
110      * enables us to run one single animator to handle several properties in parallel. Each
111      * property is tossed onto the pending list until the animation actually starts (which is
112      * done by posting it onto mView), at which time the pending list is cleared and the properties
113      * on that list are added to the list of properties associated with that animator.
114      */
115     ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>();
116 
117     /**
118      * Constants used to associate a property being requested and the mechanism used to set
119      * the property (this class calls directly into View to set the properties in question).
120      */
121     private static final int NONE           = 0x0000;
122     private static final int TRANSLATION_X  = 0x0001;
123     private static final int TRANSLATION_Y  = 0x0002;
124     private static final int SCALE_X        = 0x0004;
125     private static final int SCALE_Y        = 0x0008;
126     private static final int ROTATION       = 0x0010;
127     private static final int ROTATION_X     = 0x0020;
128     private static final int ROTATION_Y     = 0x0040;
129     private static final int X              = 0x0080;
130     private static final int Y              = 0x0100;
131     private static final int ALPHA          = 0x0200;
132 
133     private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y |
134             ROTATION | ROTATION_X | ROTATION_Y | X | Y;
135 
136     /**
137      * The mechanism by which the user can request several properties that are then animated
138      * together works by posting this Runnable to start the underlying Animator. Every time
139      * a property animation is requested, we cancel any previous postings of the Runnable
140      * and re-post it. This means that we will only ever run the Runnable (and thus start the
141      * underlying animator) after the caller is done setting the properties that should be
142      * animated together.
143      */
144     private Runnable mAnimationStarter = new Runnable() {
145         @Override
146         public void run() {
147             startAnimation();
148         }
149     };
150 
151     /**
152      * This class holds information about the overall animation being run on the set of
153      * properties. The mask describes which properties are being animated and the
154      * values holder is the list of all property/value objects.
155      */
156     private static class PropertyBundle {
157         int mPropertyMask;
158         ArrayList<NameValuesHolder> mNameValuesHolder;
159 
PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder)160         PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) {
161             mPropertyMask = propertyMask;
162             mNameValuesHolder = nameValuesHolder;
163         }
164 
165         /**
166          * Removes the given property from being animated as a part of this
167          * PropertyBundle. If the property was a part of this bundle, it returns
168          * true to indicate that it was, in fact, canceled. This is an indication
169          * to the caller that a cancellation actually occurred.
170          *
171          * @param propertyConstant The property whose cancellation is requested.
172          * @return true if the given property is a part of this bundle and if it
173          * has therefore been canceled.
174          */
cancel(int propertyConstant)175         boolean cancel(int propertyConstant) {
176             if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) {
177                 int count = mNameValuesHolder.size();
178                 for (int i = 0; i < count; ++i) {
179                     NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i);
180                     if (nameValuesHolder.mNameConstant == propertyConstant) {
181                         mNameValuesHolder.remove(i);
182                         mPropertyMask &= ~propertyConstant;
183                         return true;
184                     }
185                 }
186             }
187             return false;
188         }
189     }
190 
191     /**
192      * This list tracks the list of properties being animated by any particular animator.
193      * In most situations, there would only ever be one animator running at a time. But it is
194      * possible to request some properties to animate together, then while those properties
195      * are animating, to request some other properties to animate together. The way that
196      * works is by having this map associate the group of properties being animated with the
197      * animator handling the animation. On every update event for an Animator, we ask the
198      * map for the associated properties and set them accordingly.
199      */
200     private HashMap<Animator, PropertyBundle> mAnimatorMap =
201             new HashMap<Animator, PropertyBundle>();
202 
203     /**
204      * This is the information we need to set each property during the animation.
205      * mNameConstant is used to set the appropriate field in View, and the from/delta
206      * values are used to calculate the animated value for a given animation fraction
207      * during the animation.
208      */
209     private static class NameValuesHolder {
210         int mNameConstant;
211         float mFromValue;
212         float mDeltaValue;
NameValuesHolder(int nameConstant, float fromValue, float deltaValue)213         NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {
214             mNameConstant = nameConstant;
215             mFromValue = fromValue;
216             mDeltaValue = deltaValue;
217         }
218     }
219 
220     /**
221      * Constructor, called by View. This is private by design, as the user should only
222      * get a ViewPropertyAnimator by calling View.animate().
223      *
224      * @param view The View associated with this ViewPropertyAnimator
225      */
ViewPropertyAnimator(View view)226     ViewPropertyAnimator(View view) {
227         mView = view;
228         view.ensureTransformationInfo();
229     }
230 
231     /**
232      * Sets the duration for the underlying animator that animates the requested properties.
233      * By default, the animator uses the default value for ValueAnimator. Calling this method
234      * will cause the declared value to be used instead.
235      * @param duration The length of ensuing property animations, in milliseconds. The value
236      * cannot be negative.
237      * @return This object, allowing calls to methods in this class to be chained.
238      */
setDuration(long duration)239     public ViewPropertyAnimator setDuration(long duration) {
240         if (duration < 0) {
241             throw new IllegalArgumentException("Animators cannot have negative duration: " +
242                     duration);
243         }
244         mDurationSet = true;
245         mDuration = duration;
246         return this;
247     }
248 
249     /**
250      * Returns the current duration of property animations. If the duration was set on this
251      * object, that value is returned. Otherwise, the default value of the underlying Animator
252      * is returned.
253      *
254      * @see #setDuration(long)
255      * @return The duration of animations, in milliseconds.
256      */
getDuration()257     public long getDuration() {
258         if (mDurationSet) {
259             return mDuration;
260         } else {
261             // Just return the default from ValueAnimator, since that's what we'd get if
262             // the value has not been set otherwise
263             return new ValueAnimator().getDuration();
264         }
265     }
266 
267     /**
268      * Returns the current startDelay of property animations. If the startDelay was set on this
269      * object, that value is returned. Otherwise, the default value of the underlying Animator
270      * is returned.
271      *
272      * @see #setStartDelay(long)
273      * @return The startDelay of animations, in milliseconds.
274      */
getStartDelay()275     public long getStartDelay() {
276         if (mStartDelaySet) {
277             return mStartDelay;
278         } else {
279             // Just return the default from ValueAnimator (0), since that's what we'd get if
280             // the value has not been set otherwise
281             return 0;
282         }
283     }
284 
285     /**
286      * Sets the startDelay for the underlying animator that animates the requested properties.
287      * By default, the animator uses the default value for ValueAnimator. Calling this method
288      * will cause the declared value to be used instead.
289      * @param startDelay The delay of ensuing property animations, in milliseconds. The value
290      * cannot be negative.
291      * @return This object, allowing calls to methods in this class to be chained.
292      */
setStartDelay(long startDelay)293     public ViewPropertyAnimator setStartDelay(long startDelay) {
294         if (startDelay < 0) {
295             throw new IllegalArgumentException("Animators cannot have negative duration: " +
296                     startDelay);
297         }
298         mStartDelaySet = true;
299         mStartDelay = startDelay;
300         return this;
301     }
302 
303     /**
304      * Sets the interpolator for the underlying animator that animates the requested properties.
305      * By default, the animator uses the default interpolator for ValueAnimator. Calling this method
306      * will cause the declared object to be used instead.
307      *
308      * @param interpolator The TimeInterpolator to be used for ensuing property animations.
309      * @return This object, allowing calls to methods in this class to be chained.
310      */
setInterpolator(TimeInterpolator interpolator)311     public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) {
312         mInterpolatorSet = true;
313         mInterpolator = interpolator;
314         return this;
315     }
316 
317     /**
318      * Sets a listener for events in the underlying Animators that run the property
319      * animations.
320      *
321      * @param listener The listener to be called with AnimatorListener events.
322      * @return This object, allowing calls to methods in this class to be chained.
323      */
setListener(Animator.AnimatorListener listener)324     public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
325         mListener = listener;
326         return this;
327     }
328 
329     /**
330      * Starts the currently pending property animations immediately. Calling <code>start()</code>
331      * is optional because all animations start automatically at the next opportunity. However,
332      * if the animations are needed to start immediately and synchronously (not at the time when
333      * the next event is processed by the hierarchy, which is when the animations would begin
334      * otherwise), then this method can be used.
335      */
start()336     public void start() {
337         startAnimation();
338     }
339 
340     /**
341      * Cancels all property animations that are currently running or pending.
342      */
cancel()343     public void cancel() {
344         if (mAnimatorMap.size() > 0) {
345             HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
346                     (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone();
347             Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
348             for (Animator runningAnim : animatorSet) {
349                 runningAnim.cancel();
350             }
351         }
352         mPendingAnimations.clear();
353         mView.removeCallbacks(mAnimationStarter);
354     }
355 
356     /**
357      * This method will cause the View's <code>x</code> property to be animated to the
358      * specified value. Animations already running on the property will be canceled.
359      *
360      * @param value The value to be animated to.
361      * @see View#setX(float)
362      * @return This object, allowing calls to methods in this class to be chained.
363      */
x(float value)364     public ViewPropertyAnimator x(float value) {
365         animateProperty(X, value);
366         return this;
367     }
368 
369     /**
370      * This method will cause the View's <code>x</code> property to be animated by the
371      * specified value. Animations already running on the property will be canceled.
372      *
373      * @param value The amount to be animated by, as an offset from the current value.
374      * @see View#setX(float)
375      * @return This object, allowing calls to methods in this class to be chained.
376      */
xBy(float value)377     public ViewPropertyAnimator xBy(float value) {
378         animatePropertyBy(X, value);
379         return this;
380     }
381 
382     /**
383      * This method will cause the View's <code>y</code> property to be animated to the
384      * specified value. Animations already running on the property will be canceled.
385      *
386      * @param value The value to be animated to.
387      * @see View#setY(float)
388      * @return This object, allowing calls to methods in this class to be chained.
389      */
y(float value)390     public ViewPropertyAnimator y(float value) {
391         animateProperty(Y, value);
392         return this;
393     }
394 
395     /**
396      * This method will cause the View's <code>y</code> property to be animated by the
397      * specified value. Animations already running on the property will be canceled.
398      *
399      * @param value The amount to be animated by, as an offset from the current value.
400      * @see View#setY(float)
401      * @return This object, allowing calls to methods in this class to be chained.
402      */
yBy(float value)403     public ViewPropertyAnimator yBy(float value) {
404         animatePropertyBy(Y, value);
405         return this;
406     }
407 
408     /**
409      * This method will cause the View's <code>rotation</code> property to be animated to the
410      * specified value. Animations already running on the property will be canceled.
411      *
412      * @param value The value to be animated to.
413      * @see View#setRotation(float)
414      * @return This object, allowing calls to methods in this class to be chained.
415      */
rotation(float value)416     public ViewPropertyAnimator rotation(float value) {
417         animateProperty(ROTATION, value);
418         return this;
419     }
420 
421     /**
422      * This method will cause the View's <code>rotation</code> property to be animated by the
423      * specified value. Animations already running on the property will be canceled.
424      *
425      * @param value The amount to be animated by, as an offset from the current value.
426      * @see View#setRotation(float)
427      * @return This object, allowing calls to methods in this class to be chained.
428      */
rotationBy(float value)429     public ViewPropertyAnimator rotationBy(float value) {
430         animatePropertyBy(ROTATION, value);
431         return this;
432     }
433 
434     /**
435      * This method will cause the View's <code>rotationX</code> property to be animated to the
436      * specified value. Animations already running on the property will be canceled.
437      *
438      * @param value The value to be animated to.
439      * @see View#setRotationX(float)
440      * @return This object, allowing calls to methods in this class to be chained.
441      */
rotationX(float value)442     public ViewPropertyAnimator rotationX(float value) {
443         animateProperty(ROTATION_X, value);
444         return this;
445     }
446 
447     /**
448      * This method will cause the View's <code>rotationX</code> property to be animated by the
449      * specified value. Animations already running on the property will be canceled.
450      *
451      * @param value The amount to be animated by, as an offset from the current value.
452      * @see View#setRotationX(float)
453      * @return This object, allowing calls to methods in this class to be chained.
454      */
rotationXBy(float value)455     public ViewPropertyAnimator rotationXBy(float value) {
456         animatePropertyBy(ROTATION_X, value);
457         return this;
458     }
459 
460     /**
461      * This method will cause the View's <code>rotationY</code> property to be animated to the
462      * specified value. Animations already running on the property will be canceled.
463      *
464      * @param value The value to be animated to.
465      * @see View#setRotationY(float)
466      * @return This object, allowing calls to methods in this class to be chained.
467      */
rotationY(float value)468     public ViewPropertyAnimator rotationY(float value) {
469         animateProperty(ROTATION_Y, value);
470         return this;
471     }
472 
473     /**
474      * This method will cause the View's <code>rotationY</code> property to be animated by the
475      * specified value. Animations already running on the property will be canceled.
476      *
477      * @param value The amount to be animated by, as an offset from the current value.
478      * @see View#setRotationY(float)
479      * @return This object, allowing calls to methods in this class to be chained.
480      */
rotationYBy(float value)481     public ViewPropertyAnimator rotationYBy(float value) {
482         animatePropertyBy(ROTATION_Y, value);
483         return this;
484     }
485 
486     /**
487      * This method will cause the View's <code>translationX</code> property to be animated to the
488      * specified value. Animations already running on the property will be canceled.
489      *
490      * @param value The value to be animated to.
491      * @see View#setTranslationX(float)
492      * @return This object, allowing calls to methods in this class to be chained.
493      */
translationX(float value)494     public ViewPropertyAnimator translationX(float value) {
495         animateProperty(TRANSLATION_X, value);
496         return this;
497     }
498 
499     /**
500      * This method will cause the View's <code>translationX</code> property to be animated by the
501      * specified value. Animations already running on the property will be canceled.
502      *
503      * @param value The amount to be animated by, as an offset from the current value.
504      * @see View#setTranslationX(float)
505      * @return This object, allowing calls to methods in this class to be chained.
506      */
translationXBy(float value)507     public ViewPropertyAnimator translationXBy(float value) {
508         animatePropertyBy(TRANSLATION_X, value);
509         return this;
510     }
511 
512     /**
513      * This method will cause the View's <code>translationY</code> property to be animated to the
514      * specified value. Animations already running on the property will be canceled.
515      *
516      * @param value The value to be animated to.
517      * @see View#setTranslationY(float)
518      * @return This object, allowing calls to methods in this class to be chained.
519      */
translationY(float value)520     public ViewPropertyAnimator translationY(float value) {
521         animateProperty(TRANSLATION_Y, value);
522         return this;
523     }
524 
525     /**
526      * This method will cause the View's <code>translationY</code> property to be animated by the
527      * specified value. Animations already running on the property will be canceled.
528      *
529      * @param value The amount to be animated by, as an offset from the current value.
530      * @see View#setTranslationY(float)
531      * @return This object, allowing calls to methods in this class to be chained.
532      */
translationYBy(float value)533     public ViewPropertyAnimator translationYBy(float value) {
534         animatePropertyBy(TRANSLATION_Y, value);
535         return this;
536     }
537 
538     /**
539      * This method will cause the View's <code>scaleX</code> property to be animated to the
540      * specified value. Animations already running on the property will be canceled.
541      *
542      * @param value The value to be animated to.
543      * @see View#setScaleX(float)
544      * @return This object, allowing calls to methods in this class to be chained.
545      */
scaleX(float value)546     public ViewPropertyAnimator scaleX(float value) {
547         animateProperty(SCALE_X, value);
548         return this;
549     }
550 
551     /**
552      * This method will cause the View's <code>scaleX</code> property to be animated by the
553      * specified value. Animations already running on the property will be canceled.
554      *
555      * @param value The amount to be animated by, as an offset from the current value.
556      * @see View#setScaleX(float)
557      * @return This object, allowing calls to methods in this class to be chained.
558      */
scaleXBy(float value)559     public ViewPropertyAnimator scaleXBy(float value) {
560         animatePropertyBy(SCALE_X, value);
561         return this;
562     }
563 
564     /**
565      * This method will cause the View's <code>scaleY</code> property to be animated to the
566      * specified value. Animations already running on the property will be canceled.
567      *
568      * @param value The value to be animated to.
569      * @see View#setScaleY(float)
570      * @return This object, allowing calls to methods in this class to be chained.
571      */
scaleY(float value)572     public ViewPropertyAnimator scaleY(float value) {
573         animateProperty(SCALE_Y, value);
574         return this;
575     }
576 
577     /**
578      * This method will cause the View's <code>scaleY</code> property to be animated by the
579      * specified value. Animations already running on the property will be canceled.
580      *
581      * @param value The amount to be animated by, as an offset from the current value.
582      * @see View#setScaleY(float)
583      * @return This object, allowing calls to methods in this class to be chained.
584      */
scaleYBy(float value)585     public ViewPropertyAnimator scaleYBy(float value) {
586         animatePropertyBy(SCALE_Y, value);
587         return this;
588     }
589 
590     /**
591      * This method will cause the View's <code>alpha</code> property to be animated to the
592      * specified value. Animations already running on the property will be canceled.
593      *
594      * @param value The value to be animated to.
595      * @see View#setAlpha(float)
596      * @return This object, allowing calls to methods in this class to be chained.
597      */
alpha(float value)598     public ViewPropertyAnimator alpha(float value) {
599         animateProperty(ALPHA, value);
600         return this;
601     }
602 
603     /**
604      * This method will cause the View's <code>alpha</code> property to be animated by the
605      * specified value. Animations already running on the property will be canceled.
606      *
607      * @param value The amount to be animated by, as an offset from the current value.
608      * @see View#setAlpha(float)
609      * @return This object, allowing calls to methods in this class to be chained.
610      */
alphaBy(float value)611     public ViewPropertyAnimator alphaBy(float value) {
612         animatePropertyBy(ALPHA, value);
613         return this;
614     }
615 
616     /**
617      * Starts the underlying Animator for a set of properties. We use a single animator that
618      * simply runs from 0 to 1, and then use that fractional value to set each property
619      * value accordingly.
620      */
startAnimation()621     private void startAnimation() {
622         ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
623         ArrayList<NameValuesHolder> nameValueList =
624                 (ArrayList<NameValuesHolder>) mPendingAnimations.clone();
625         mPendingAnimations.clear();
626         int propertyMask = 0;
627         int propertyCount = nameValueList.size();
628         for (int i = 0; i < propertyCount; ++i) {
629             NameValuesHolder nameValuesHolder = nameValueList.get(i);
630             propertyMask |= nameValuesHolder.mNameConstant;
631         }
632         mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
633         animator.addUpdateListener(mAnimatorEventListener);
634         animator.addListener(mAnimatorEventListener);
635         if (mStartDelaySet) {
636             animator.setStartDelay(mStartDelay);
637         }
638         if (mDurationSet) {
639             animator.setDuration(mDuration);
640         }
641         if (mInterpolatorSet) {
642             animator.setInterpolator(mInterpolator);
643         }
644         animator.start();
645     }
646 
647     /**
648      * Utility function, called by the various x(), y(), etc. methods. This stores the
649      * constant name for the property along with the from/delta values that will be used to
650      * calculate and set the property during the animation. This structure is added to the
651      * pending animations, awaiting the eventual start() of the underlying animator. A
652      * Runnable is posted to start the animation, and any pending such Runnable is canceled
653      * (which enables us to end up starting just one animator for all of the properties
654      * specified at one time).
655      *
656      * @param constantName The specifier for the property being animated
657      * @param toValue The value to which the property will animate
658      */
animateProperty(int constantName, float toValue)659     private void animateProperty(int constantName, float toValue) {
660         float fromValue = getValue(constantName);
661         float deltaValue = toValue - fromValue;
662         animatePropertyBy(constantName, fromValue, deltaValue);
663     }
664 
665     /**
666      * Utility function, called by the various xBy(), yBy(), etc. methods. This method is
667      * just like animateProperty(), except the value is an offset from the property's
668      * current value, instead of an absolute "to" value.
669      *
670      * @param constantName The specifier for the property being animated
671      * @param byValue The amount by which the property will change
672      */
animatePropertyBy(int constantName, float byValue)673     private void animatePropertyBy(int constantName, float byValue) {
674         float fromValue = getValue(constantName);
675         animatePropertyBy(constantName, fromValue, byValue);
676     }
677 
678     /**
679      * Utility function, called by animateProperty() and animatePropertyBy(), which handles the
680      * details of adding a pending animation and posting the request to start the animation.
681      *
682      * @param constantName The specifier for the property being animated
683      * @param startValue The starting value of the property
684      * @param byValue The amount by which the property will change
685      */
animatePropertyBy(int constantName, float startValue, float byValue)686     private void animatePropertyBy(int constantName, float startValue, float byValue) {
687         // First, cancel any existing animations on this property
688         if (mAnimatorMap.size() > 0) {
689             Animator animatorToCancel = null;
690             Set<Animator> animatorSet = mAnimatorMap.keySet();
691             for (Animator runningAnim : animatorSet) {
692                 PropertyBundle bundle = mAnimatorMap.get(runningAnim);
693                 if (bundle.cancel(constantName)) {
694                     // property was canceled - cancel the animation if it's now empty
695                     // Note that it's safe to break out here because every new animation
696                     // on a property will cancel a previous animation on that property, so
697                     // there can only ever be one such animation running.
698                     if (bundle.mPropertyMask == NONE) {
699                         // the animation is no longer changing anything - cancel it
700                         animatorToCancel = runningAnim;
701                         break;
702                     }
703                 }
704             }
705             if (animatorToCancel != null) {
706                 animatorToCancel.cancel();
707             }
708         }
709 
710         NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
711         mPendingAnimations.add(nameValuePair);
712         mView.removeCallbacks(mAnimationStarter);
713         mView.post(mAnimationStarter);
714     }
715 
716     /**
717      * This method handles setting the property values directly in the View object's fields.
718      * propertyConstant tells it which property should be set, value is the value to set
719      * the property to.
720      *
721      * @param propertyConstant The property to be set
722      * @param value The value to set the property to
723      */
setValue(int propertyConstant, float value)724     private void setValue(int propertyConstant, float value) {
725         final View.TransformationInfo info = mView.mTransformationInfo;
726         switch (propertyConstant) {
727             case TRANSLATION_X:
728                 info.mTranslationX = value;
729                 break;
730             case TRANSLATION_Y:
731                 info.mTranslationY = value;
732                 break;
733             case ROTATION:
734                 info.mRotation = value;
735                 break;
736             case ROTATION_X:
737                 info.mRotationX = value;
738                 break;
739             case ROTATION_Y:
740                 info.mRotationY = value;
741                 break;
742             case SCALE_X:
743                 info.mScaleX = value;
744                 break;
745             case SCALE_Y:
746                 info.mScaleY = value;
747                 break;
748             case X:
749                 info.mTranslationX = value - mView.mLeft;
750                 break;
751             case Y:
752                 info.mTranslationY = value - mView.mTop;
753                 break;
754             case ALPHA:
755                 info.mAlpha = value;
756                 break;
757         }
758     }
759 
760     /**
761      * This method gets the value of the named property from the View object.
762      *
763      * @param propertyConstant The property whose value should be returned
764      * @return float The value of the named property
765      */
getValue(int propertyConstant)766     private float getValue(int propertyConstant) {
767         final View.TransformationInfo info = mView.mTransformationInfo;
768         switch (propertyConstant) {
769             case TRANSLATION_X:
770                 return info.mTranslationX;
771             case TRANSLATION_Y:
772                 return info.mTranslationY;
773             case ROTATION:
774                 return info.mRotation;
775             case ROTATION_X:
776                 return info.mRotationX;
777             case ROTATION_Y:
778                 return info.mRotationY;
779             case SCALE_X:
780                 return info.mScaleX;
781             case SCALE_Y:
782                 return info.mScaleY;
783             case X:
784                 return mView.mLeft + info.mTranslationX;
785             case Y:
786                 return mView.mTop + info.mTranslationY;
787             case ALPHA:
788                 return info.mAlpha;
789         }
790         return 0;
791     }
792 
793     /**
794      * Utility class that handles the various Animator events. The only ones we care
795      * about are the end event (which we use to clean up the animator map when an animator
796      * finishes) and the update event (which we use to calculate the current value of each
797      * property and then set it on the view object).
798      */
799     private class AnimatorEventListener
800             implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
801         @Override
onAnimationStart(Animator animation)802         public void onAnimationStart(Animator animation) {
803             if (mListener != null) {
804                 mListener.onAnimationStart(animation);
805             }
806         }
807 
808         @Override
onAnimationCancel(Animator animation)809         public void onAnimationCancel(Animator animation) {
810             if (mListener != null) {
811                 mListener.onAnimationCancel(animation);
812             }
813         }
814 
815         @Override
onAnimationRepeat(Animator animation)816         public void onAnimationRepeat(Animator animation) {
817             if (mListener != null) {
818                 mListener.onAnimationRepeat(animation);
819             }
820         }
821 
822         @Override
onAnimationEnd(Animator animation)823         public void onAnimationEnd(Animator animation) {
824             if (mListener != null) {
825                 mListener.onAnimationEnd(animation);
826             }
827             mAnimatorMap.remove(animation);
828         }
829 
830         /**
831          * Calculate the current value for each property and set it on the view. Invalidate
832          * the view object appropriately, depending on which properties are being animated.
833          *
834          * @param animation The animator associated with the properties that need to be
835          * set. This animator holds the animation fraction which we will use to calculate
836          * the current value of each property.
837          */
838         @Override
onAnimationUpdate(ValueAnimator animation)839         public void onAnimationUpdate(ValueAnimator animation) {
840             // alpha requires slightly different treatment than the other (transform) properties.
841             // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
842             // logic is dependent on how the view handles an internal call to onSetAlpha().
843             // We track what kinds of properties are set, and how alpha is handled when it is
844             // set, and perform the invalidation steps appropriately.
845             boolean alphaHandled = false;
846             mView.invalidateParentCaches();
847             float fraction = animation.getAnimatedFraction();
848             PropertyBundle propertyBundle = mAnimatorMap.get(animation);
849             int propertyMask = propertyBundle.mPropertyMask;
850             if ((propertyMask & TRANSFORM_MASK) != 0) {
851                 mView.invalidate(false);
852             }
853             ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
854             if (valueList != null) {
855                 int count = valueList.size();
856                 for (int i = 0; i < count; ++i) {
857                     NameValuesHolder values = valueList.get(i);
858                     float value = values.mFromValue + fraction * values.mDeltaValue;
859                     if (values.mNameConstant == ALPHA) {
860                         alphaHandled = mView.setAlphaNoInvalidation(value);
861                     } else {
862                         setValue(values.mNameConstant, value);
863                     }
864                 }
865             }
866             if ((propertyMask & TRANSFORM_MASK) != 0) {
867                 mView.mTransformationInfo.mMatrixDirty = true;
868                 mView.mPrivateFlags |= View.DRAWN; // force another invalidation
869             }
870             // invalidate(false) in all cases except if alphaHandled gets set to true
871             // via the call to setAlphaNoInvalidation(), above
872             mView.invalidate(alphaHandled);
873         }
874     }
875 }
876