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