Home | History | Annotate | Download | only in view
      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 
    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          */
    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;
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    336     public void start() {
    337         startAnimation();
    338     }
    339 
    340     /**
    341      * Cancels all property animations that are currently running or pending.
    342      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    802         public void onAnimationStart(Animator animation) {
    803             if (mListener != null) {
    804                 mListener.onAnimationStart(animation);
    805             }
    806         }
    807 
    808         @Override
    809         public void onAnimationCancel(Animator animation) {
    810             if (mListener != null) {
    811                 mListener.onAnimationCancel(animation);
    812             }
    813         }
    814 
    815         @Override
    816         public void onAnimationRepeat(Animator animation) {
    817             if (mListener != null) {
    818                 mListener.onAnimationRepeat(animation);
    819             }
    820         }
    821 
    822         @Override
    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
    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