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