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      * Sets a listener for events in the underlying Animators that run the property
    327      * animations.
    328      *
    329      * @param listener The listener to be called with AnimatorListener events.
    330      * @return This object, allowing calls to methods in this class to be chained.
    331      */
    332     public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
    333         mListener = listener;
    334         return this;
    335     }
    336 
    337     /**
    338      * Starts the currently pending property animations immediately. Calling <code>start()</code>
    339      * is optional because all animations start automatically at the next opportunity. However,
    340      * if the animations are needed to start immediately and synchronously (not at the time when
    341      * the next event is processed by the hierarchy, which is when the animations would begin
    342      * otherwise), then this method can be used.
    343      */
    344     public void start() {
    345         mView.removeCallbacks(mAnimationStarter);
    346         startAnimation();
    347     }
    348 
    349     /**
    350      * Cancels all property animations that are currently running or pending.
    351      */
    352     public void cancel() {
    353         if (mAnimatorMap.size() > 0) {
    354             HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
    355                     (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone();
    356             Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
    357             for (Animator runningAnim : animatorSet) {
    358                 runningAnim.cancel();
    359             }
    360         }
    361         mPendingAnimations.clear();
    362         mView.removeCallbacks(mAnimationStarter);
    363     }
    364 
    365     /**
    366      * This method will cause the View's <code>x</code> property to be animated to the
    367      * specified value. Animations already running on the property will be canceled.
    368      *
    369      * @param value The value to be animated to.
    370      * @see View#setX(float)
    371      * @return This object, allowing calls to methods in this class to be chained.
    372      */
    373     public ViewPropertyAnimator x(float value) {
    374         animateProperty(X, value);
    375         return this;
    376     }
    377 
    378     /**
    379      * This method will cause the View's <code>x</code> property to be animated by the
    380      * specified value. Animations already running on the property will be canceled.
    381      *
    382      * @param value The amount to be animated by, as an offset from the current value.
    383      * @see View#setX(float)
    384      * @return This object, allowing calls to methods in this class to be chained.
    385      */
    386     public ViewPropertyAnimator xBy(float value) {
    387         animatePropertyBy(X, value);
    388         return this;
    389     }
    390 
    391     /**
    392      * This method will cause the View's <code>y</code> property to be animated to the
    393      * specified value. Animations already running on the property will be canceled.
    394      *
    395      * @param value The value to be animated to.
    396      * @see View#setY(float)
    397      * @return This object, allowing calls to methods in this class to be chained.
    398      */
    399     public ViewPropertyAnimator y(float value) {
    400         animateProperty(Y, value);
    401         return this;
    402     }
    403 
    404     /**
    405      * This method will cause the View's <code>y</code> property to be animated by the
    406      * specified value. Animations already running on the property will be canceled.
    407      *
    408      * @param value The amount to be animated by, as an offset from the current value.
    409      * @see View#setY(float)
    410      * @return This object, allowing calls to methods in this class to be chained.
    411      */
    412     public ViewPropertyAnimator yBy(float value) {
    413         animatePropertyBy(Y, value);
    414         return this;
    415     }
    416 
    417     /**
    418      * This method will cause the View's <code>rotation</code> property to be animated to the
    419      * specified value. Animations already running on the property will be canceled.
    420      *
    421      * @param value The value to be animated to.
    422      * @see View#setRotation(float)
    423      * @return This object, allowing calls to methods in this class to be chained.
    424      */
    425     public ViewPropertyAnimator rotation(float value) {
    426         animateProperty(ROTATION, value);
    427         return this;
    428     }
    429 
    430     /**
    431      * This method will cause the View's <code>rotation</code> property to be animated by the
    432      * specified value. Animations already running on the property will be canceled.
    433      *
    434      * @param value The amount to be animated by, as an offset from the current value.
    435      * @see View#setRotation(float)
    436      * @return This object, allowing calls to methods in this class to be chained.
    437      */
    438     public ViewPropertyAnimator rotationBy(float value) {
    439         animatePropertyBy(ROTATION, value);
    440         return this;
    441     }
    442 
    443     /**
    444      * This method will cause the View's <code>rotationX</code> property to be animated to the
    445      * specified value. Animations already running on the property will be canceled.
    446      *
    447      * @param value The value to be animated to.
    448      * @see View#setRotationX(float)
    449      * @return This object, allowing calls to methods in this class to be chained.
    450      */
    451     public ViewPropertyAnimator rotationX(float value) {
    452         animateProperty(ROTATION_X, value);
    453         return this;
    454     }
    455 
    456     /**
    457      * This method will cause the View's <code>rotationX</code> property to be animated by the
    458      * specified value. Animations already running on the property will be canceled.
    459      *
    460      * @param value The amount to be animated by, as an offset from the current value.
    461      * @see View#setRotationX(float)
    462      * @return This object, allowing calls to methods in this class to be chained.
    463      */
    464     public ViewPropertyAnimator rotationXBy(float value) {
    465         animatePropertyBy(ROTATION_X, value);
    466         return this;
    467     }
    468 
    469     /**
    470      * This method will cause the View's <code>rotationY</code> property to be animated to the
    471      * specified value. Animations already running on the property will be canceled.
    472      *
    473      * @param value The value to be animated to.
    474      * @see View#setRotationY(float)
    475      * @return This object, allowing calls to methods in this class to be chained.
    476      */
    477     public ViewPropertyAnimator rotationY(float value) {
    478         animateProperty(ROTATION_Y, value);
    479         return this;
    480     }
    481 
    482     /**
    483      * This method will cause the View's <code>rotationY</code> property to be animated by the
    484      * specified value. Animations already running on the property will be canceled.
    485      *
    486      * @param value The amount to be animated by, as an offset from the current value.
    487      * @see View#setRotationY(float)
    488      * @return This object, allowing calls to methods in this class to be chained.
    489      */
    490     public ViewPropertyAnimator rotationYBy(float value) {
    491         animatePropertyBy(ROTATION_Y, value);
    492         return this;
    493     }
    494 
    495     /**
    496      * This method will cause the View's <code>translationX</code> property to be animated to the
    497      * specified value. Animations already running on the property will be canceled.
    498      *
    499      * @param value The value to be animated to.
    500      * @see View#setTranslationX(float)
    501      * @return This object, allowing calls to methods in this class to be chained.
    502      */
    503     public ViewPropertyAnimator translationX(float value) {
    504         animateProperty(TRANSLATION_X, value);
    505         return this;
    506     }
    507 
    508     /**
    509      * This method will cause the View's <code>translationX</code> property to be animated by the
    510      * specified value. Animations already running on the property will be canceled.
    511      *
    512      * @param value The amount to be animated by, as an offset from the current value.
    513      * @see View#setTranslationX(float)
    514      * @return This object, allowing calls to methods in this class to be chained.
    515      */
    516     public ViewPropertyAnimator translationXBy(float value) {
    517         animatePropertyBy(TRANSLATION_X, value);
    518         return this;
    519     }
    520 
    521     /**
    522      * This method will cause the View's <code>translationY</code> property to be animated to the
    523      * specified value. Animations already running on the property will be canceled.
    524      *
    525      * @param value The value to be animated to.
    526      * @see View#setTranslationY(float)
    527      * @return This object, allowing calls to methods in this class to be chained.
    528      */
    529     public ViewPropertyAnimator translationY(float value) {
    530         animateProperty(TRANSLATION_Y, value);
    531         return this;
    532     }
    533 
    534     /**
    535      * This method will cause the View's <code>translationY</code> property to be animated by the
    536      * specified value. Animations already running on the property will be canceled.
    537      *
    538      * @param value The amount to be animated by, as an offset from the current value.
    539      * @see View#setTranslationY(float)
    540      * @return This object, allowing calls to methods in this class to be chained.
    541      */
    542     public ViewPropertyAnimator translationYBy(float value) {
    543         animatePropertyBy(TRANSLATION_Y, value);
    544         return this;
    545     }
    546 
    547     /**
    548      * This method will cause the View's <code>scaleX</code> property to be animated to the
    549      * specified value. Animations already running on the property will be canceled.
    550      *
    551      * @param value The value to be animated to.
    552      * @see View#setScaleX(float)
    553      * @return This object, allowing calls to methods in this class to be chained.
    554      */
    555     public ViewPropertyAnimator scaleX(float value) {
    556         animateProperty(SCALE_X, value);
    557         return this;
    558     }
    559 
    560     /**
    561      * This method will cause the View's <code>scaleX</code> property to be animated by the
    562      * specified value. Animations already running on the property will be canceled.
    563      *
    564      * @param value The amount to be animated by, as an offset from the current value.
    565      * @see View#setScaleX(float)
    566      * @return This object, allowing calls to methods in this class to be chained.
    567      */
    568     public ViewPropertyAnimator scaleXBy(float value) {
    569         animatePropertyBy(SCALE_X, value);
    570         return this;
    571     }
    572 
    573     /**
    574      * This method will cause the View's <code>scaleY</code> property to be animated to the
    575      * specified value. Animations already running on the property will be canceled.
    576      *
    577      * @param value The value to be animated to.
    578      * @see View#setScaleY(float)
    579      * @return This object, allowing calls to methods in this class to be chained.
    580      */
    581     public ViewPropertyAnimator scaleY(float value) {
    582         animateProperty(SCALE_Y, value);
    583         return this;
    584     }
    585 
    586     /**
    587      * This method will cause the View's <code>scaleY</code> property to be animated by the
    588      * specified value. Animations already running on the property will be canceled.
    589      *
    590      * @param value The amount to be animated by, as an offset from the current value.
    591      * @see View#setScaleY(float)
    592      * @return This object, allowing calls to methods in this class to be chained.
    593      */
    594     public ViewPropertyAnimator scaleYBy(float value) {
    595         animatePropertyBy(SCALE_Y, value);
    596         return this;
    597     }
    598 
    599     /**
    600      * This method will cause the View's <code>alpha</code> property to be animated to the
    601      * specified value. Animations already running on the property will be canceled.
    602      *
    603      * @param value The value to be animated to.
    604      * @see View#setAlpha(float)
    605      * @return This object, allowing calls to methods in this class to be chained.
    606      */
    607     public ViewPropertyAnimator alpha(float value) {
    608         animateProperty(ALPHA, value);
    609         return this;
    610     }
    611 
    612     /**
    613      * This method will cause the View's <code>alpha</code> property to be animated by the
    614      * specified value. Animations already running on the property will be canceled.
    615      *
    616      * @param value The amount to be animated by, as an offset from the current value.
    617      * @see View#setAlpha(float)
    618      * @return This object, allowing calls to methods in this class to be chained.
    619      */
    620     public ViewPropertyAnimator alphaBy(float value) {
    621         animatePropertyBy(ALPHA, value);
    622         return this;
    623     }
    624 
    625     /**
    626      * The View associated with this ViewPropertyAnimator will have its
    627      * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to
    628      * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation.
    629      * As stated in the documentation for {@link View#LAYER_TYPE_HARDWARE},
    630      * the actual type of layer used internally depends on the runtime situation of the
    631      * view. If the activity and this view are hardware-accelerated, then the layer will be
    632      * accelerated as well. If the activity or the view is not accelerated, then the layer will
    633      * effectively be the same as {@link View#LAYER_TYPE_SOFTWARE}.
    634      *
    635      * <p>This state is not persistent, either on the View or on this ViewPropertyAnimator: the
    636      * layer type of the View will be restored when the animation ends to what it was when this
    637      * method was called, and this setting on ViewPropertyAnimator is only valid for the next
    638      * animation. Note that calling this method and then independently setting the layer type of
    639      * the View (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will
    640      * result in some inconsistency, including having the layer type restored to its pre-withLayer()
    641      * value when the animation ends.</p>
    642      *
    643      * @see View#setLayerType(int, android.graphics.Paint)
    644      * @return This object, allowing calls to methods in this class to be chained.
    645      */
    646     public ViewPropertyAnimator withLayer() {
    647          mPendingSetupAction= new Runnable() {
    648             @Override
    649             public void run() {
    650                 mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    651             }
    652         };
    653         final int currentLayerType = mView.getLayerType();
    654         mPendingCleanupAction = new Runnable() {
    655             @Override
    656             public void run() {
    657                 mView.setLayerType(currentLayerType, null);
    658             }
    659         };
    660         if (mAnimatorSetupMap == null) {
    661             mAnimatorSetupMap = new HashMap<Animator, Runnable>();
    662         }
    663         if (mAnimatorCleanupMap == null) {
    664             mAnimatorCleanupMap = new HashMap<Animator, Runnable>();
    665         }
    666 
    667         return this;
    668     }
    669 
    670     /**
    671      * Specifies an action to take place when the next animation runs. If there is a
    672      * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the
    673      * action will run after that startDelay expires, when the actual animation begins.
    674      * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate
    675      * choreographing ViewPropertyAnimator animations with other animations or actions
    676      * in the application.
    677      *
    678      * @param runnable The action to run when the next animation starts.
    679      * @return This object, allowing calls to methods in this class to be chained.
    680      */
    681     public ViewPropertyAnimator withStartAction(Runnable runnable) {
    682         mPendingOnStartAction = runnable;
    683         if (runnable != null && mAnimatorOnStartMap == null) {
    684             mAnimatorOnStartMap = new HashMap<Animator, Runnable>();
    685         }
    686         return this;
    687     }
    688 
    689     /**
    690      * Specifies an action to take place when the next animation ends. The action is only
    691      * run if the animation ends normally; if the ViewPropertyAnimator is canceled during
    692      * that animation, the runnable will not run.
    693      * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate
    694      * choreographing ViewPropertyAnimator animations with other animations or actions
    695      * in the application.
    696      *
    697      * <p>For example, the following code animates a view to x=200 and then back to 0:</p>
    698      * <pre>
    699      *     Runnable endAction = new Runnable() {
    700      *         public void run() {
    701      *             view.animate().x(0);
    702      *         }
    703      *     };
    704      *     view.animate().x(200).onEnd(endAction);
    705      * </pre>
    706      *
    707      * @param runnable The action to run when the next animation ends.
    708      * @return This object, allowing calls to methods in this class to be chained.
    709      */
    710     public ViewPropertyAnimator withEndAction(Runnable runnable) {
    711         mPendingOnEndAction = runnable;
    712         if (runnable != null && mAnimatorOnEndMap == null) {
    713             mAnimatorOnEndMap = new HashMap<Animator, Runnable>();
    714         }
    715         return this;
    716     }
    717 
    718     /**
    719      * Starts the underlying Animator for a set of properties. We use a single animator that
    720      * simply runs from 0 to 1, and then use that fractional value to set each property
    721      * value accordingly.
    722      */
    723     private void startAnimation() {
    724         mView.setHasTransientState(true);
    725         ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
    726         ArrayList<NameValuesHolder> nameValueList =
    727                 (ArrayList<NameValuesHolder>) mPendingAnimations.clone();
    728         mPendingAnimations.clear();
    729         int propertyMask = 0;
    730         int propertyCount = nameValueList.size();
    731         for (int i = 0; i < propertyCount; ++i) {
    732             NameValuesHolder nameValuesHolder = nameValueList.get(i);
    733             propertyMask |= nameValuesHolder.mNameConstant;
    734         }
    735         mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
    736         if (mPendingSetupAction != null) {
    737             mAnimatorSetupMap.put(animator, mPendingSetupAction);
    738             mPendingSetupAction = null;
    739         }
    740         if (mPendingCleanupAction != null) {
    741             mAnimatorCleanupMap.put(animator, mPendingCleanupAction);
    742             mPendingCleanupAction = null;
    743         }
    744         if (mPendingOnStartAction != null) {
    745             mAnimatorOnStartMap.put(animator, mPendingOnStartAction);
    746             mPendingOnStartAction = null;
    747         }
    748         if (mPendingOnEndAction != null) {
    749             mAnimatorOnEndMap.put(animator, mPendingOnEndAction);
    750             mPendingOnEndAction = null;
    751         }
    752         animator.addUpdateListener(mAnimatorEventListener);
    753         animator.addListener(mAnimatorEventListener);
    754         if (mStartDelaySet) {
    755             animator.setStartDelay(mStartDelay);
    756         }
    757         if (mDurationSet) {
    758             animator.setDuration(mDuration);
    759         }
    760         if (mInterpolatorSet) {
    761             animator.setInterpolator(mInterpolator);
    762         }
    763         animator.start();
    764     }
    765 
    766     /**
    767      * Utility function, called by the various x(), y(), etc. methods. This stores the
    768      * constant name for the property along with the from/delta values that will be used to
    769      * calculate and set the property during the animation. This structure is added to the
    770      * pending animations, awaiting the eventual start() of the underlying animator. A
    771      * Runnable is posted to start the animation, and any pending such Runnable is canceled
    772      * (which enables us to end up starting just one animator for all of the properties
    773      * specified at one time).
    774      *
    775      * @param constantName The specifier for the property being animated
    776      * @param toValue The value to which the property will animate
    777      */
    778     private void animateProperty(int constantName, float toValue) {
    779         float fromValue = getValue(constantName);
    780         float deltaValue = toValue - fromValue;
    781         animatePropertyBy(constantName, fromValue, deltaValue);
    782     }
    783 
    784     /**
    785      * Utility function, called by the various xBy(), yBy(), etc. methods. This method is
    786      * just like animateProperty(), except the value is an offset from the property's
    787      * current value, instead of an absolute "to" value.
    788      *
    789      * @param constantName The specifier for the property being animated
    790      * @param byValue The amount by which the property will change
    791      */
    792     private void animatePropertyBy(int constantName, float byValue) {
    793         float fromValue = getValue(constantName);
    794         animatePropertyBy(constantName, fromValue, byValue);
    795     }
    796 
    797     /**
    798      * Utility function, called by animateProperty() and animatePropertyBy(), which handles the
    799      * details of adding a pending animation and posting the request to start the animation.
    800      *
    801      * @param constantName The specifier for the property being animated
    802      * @param startValue The starting value of the property
    803      * @param byValue The amount by which the property will change
    804      */
    805     private void animatePropertyBy(int constantName, float startValue, float byValue) {
    806         // First, cancel any existing animations on this property
    807         if (mAnimatorMap.size() > 0) {
    808             Animator animatorToCancel = null;
    809             Set<Animator> animatorSet = mAnimatorMap.keySet();
    810             for (Animator runningAnim : animatorSet) {
    811                 PropertyBundle bundle = mAnimatorMap.get(runningAnim);
    812                 if (bundle.cancel(constantName)) {
    813                     // property was canceled - cancel the animation if it's now empty
    814                     // Note that it's safe to break out here because every new animation
    815                     // on a property will cancel a previous animation on that property, so
    816                     // there can only ever be one such animation running.
    817                     if (bundle.mPropertyMask == NONE) {
    818                         // the animation is no longer changing anything - cancel it
    819                         animatorToCancel = runningAnim;
    820                         break;
    821                     }
    822                 }
    823             }
    824             if (animatorToCancel != null) {
    825                 animatorToCancel.cancel();
    826             }
    827         }
    828 
    829         NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
    830         mPendingAnimations.add(nameValuePair);
    831         mView.removeCallbacks(mAnimationStarter);
    832         mView.post(mAnimationStarter);
    833     }
    834 
    835     /**
    836      * This method handles setting the property values directly in the View object's fields.
    837      * propertyConstant tells it which property should be set, value is the value to set
    838      * the property to.
    839      *
    840      * @param propertyConstant The property to be set
    841      * @param value The value to set the property to
    842      */
    843     private void setValue(int propertyConstant, float value) {
    844         final View.TransformationInfo info = mView.mTransformationInfo;
    845         final DisplayList displayList = mView.mDisplayList;
    846         switch (propertyConstant) {
    847             case TRANSLATION_X:
    848                 info.mTranslationX = value;
    849                 if (displayList != null) displayList.setTranslationX(value);
    850                 break;
    851             case TRANSLATION_Y:
    852                 info.mTranslationY = value;
    853                 if (displayList != null) displayList.setTranslationY(value);
    854                 break;
    855             case ROTATION:
    856                 info.mRotation = value;
    857                 if (displayList != null) displayList.setRotation(value);
    858                 break;
    859             case ROTATION_X:
    860                 info.mRotationX = value;
    861                 if (displayList != null) displayList.setRotationX(value);
    862                 break;
    863             case ROTATION_Y:
    864                 info.mRotationY = value;
    865                 if (displayList != null) displayList.setRotationY(value);
    866                 break;
    867             case SCALE_X:
    868                 info.mScaleX = value;
    869                 if (displayList != null) displayList.setScaleX(value);
    870                 break;
    871             case SCALE_Y:
    872                 info.mScaleY = value;
    873                 if (displayList != null) displayList.setScaleY(value);
    874                 break;
    875             case X:
    876                 info.mTranslationX = value - mView.mLeft;
    877                 if (displayList != null) displayList.setTranslationX(value - mView.mLeft);
    878                 break;
    879             case Y:
    880                 info.mTranslationY = value - mView.mTop;
    881                 if (displayList != null) displayList.setTranslationY(value - mView.mTop);
    882                 break;
    883             case ALPHA:
    884                 info.mAlpha = value;
    885                 if (displayList != null) displayList.setAlpha(value);
    886                 break;
    887         }
    888     }
    889 
    890     /**
    891      * This method gets the value of the named property from the View object.
    892      *
    893      * @param propertyConstant The property whose value should be returned
    894      * @return float The value of the named property
    895      */
    896     private float getValue(int propertyConstant) {
    897         final View.TransformationInfo info = mView.mTransformationInfo;
    898         switch (propertyConstant) {
    899             case TRANSLATION_X:
    900                 return info.mTranslationX;
    901             case TRANSLATION_Y:
    902                 return info.mTranslationY;
    903             case ROTATION:
    904                 return info.mRotation;
    905             case ROTATION_X:
    906                 return info.mRotationX;
    907             case ROTATION_Y:
    908                 return info.mRotationY;
    909             case SCALE_X:
    910                 return info.mScaleX;
    911             case SCALE_Y:
    912                 return info.mScaleY;
    913             case X:
    914                 return mView.mLeft + info.mTranslationX;
    915             case Y:
    916                 return mView.mTop + info.mTranslationY;
    917             case ALPHA:
    918                 return info.mAlpha;
    919         }
    920         return 0;
    921     }
    922 
    923     /**
    924      * Utility class that handles the various Animator events. The only ones we care
    925      * about are the end event (which we use to clean up the animator map when an animator
    926      * finishes) and the update event (which we use to calculate the current value of each
    927      * property and then set it on the view object).
    928      */
    929     private class AnimatorEventListener
    930             implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
    931         @Override
    932         public void onAnimationStart(Animator animation) {
    933             if (mAnimatorSetupMap != null) {
    934                 Runnable r = mAnimatorSetupMap.get(animation);
    935                 if (r != null) {
    936                     r.run();
    937                 }
    938                 mAnimatorSetupMap.remove(animation);
    939             }
    940             if (mAnimatorOnStartMap != null) {
    941                 Runnable r = mAnimatorOnStartMap.get(animation);
    942                 if (r != null) {
    943                     r.run();
    944                 }
    945                 mAnimatorOnStartMap.remove(animation);
    946             }
    947             if (mListener != null) {
    948                 mListener.onAnimationStart(animation);
    949             }
    950         }
    951 
    952         @Override
    953         public void onAnimationCancel(Animator animation) {
    954             if (mListener != null) {
    955                 mListener.onAnimationCancel(animation);
    956             }
    957             if (mAnimatorOnEndMap != null) {
    958                 mAnimatorOnEndMap.remove(animation);
    959             }
    960         }
    961 
    962         @Override
    963         public void onAnimationRepeat(Animator animation) {
    964             if (mListener != null) {
    965                 mListener.onAnimationRepeat(animation);
    966             }
    967         }
    968 
    969         @Override
    970         public void onAnimationEnd(Animator animation) {
    971             mView.setHasTransientState(false);
    972             if (mListener != null) {
    973                 mListener.onAnimationEnd(animation);
    974             }
    975             if (mAnimatorOnEndMap != null) {
    976                 Runnable r = mAnimatorOnEndMap.get(animation);
    977                 if (r != null) {
    978                     r.run();
    979                 }
    980                 mAnimatorOnEndMap.remove(animation);
    981             }
    982             if (mAnimatorCleanupMap != null) {
    983                 Runnable r = mAnimatorCleanupMap.get(animation);
    984                 if (r != null) {
    985                     r.run();
    986                 }
    987                 mAnimatorCleanupMap.remove(animation);
    988             }
    989             mAnimatorMap.remove(animation);
    990         }
    991 
    992         /**
    993          * Calculate the current value for each property and set it on the view. Invalidate
    994          * the view object appropriately, depending on which properties are being animated.
    995          *
    996          * @param animation The animator associated with the properties that need to be
    997          * set. This animator holds the animation fraction which we will use to calculate
    998          * the current value of each property.
    999          */
   1000         @Override
   1001         public void onAnimationUpdate(ValueAnimator animation) {
   1002             PropertyBundle propertyBundle = mAnimatorMap.get(animation);
   1003             if (propertyBundle == null) {
   1004                 // Shouldn't happen, but just to play it safe
   1005                 return;
   1006             }
   1007             boolean useDisplayListProperties = mView.mDisplayList != null;
   1008 
   1009             // alpha requires slightly different treatment than the other (transform) properties.
   1010             // The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
   1011             // logic is dependent on how the view handles an internal call to onSetAlpha().
   1012             // We track what kinds of properties are set, and how alpha is handled when it is
   1013             // set, and perform the invalidation steps appropriately.
   1014             boolean alphaHandled = false;
   1015             if (!useDisplayListProperties) {
   1016                 mView.invalidateParentCaches();
   1017             }
   1018             float fraction = animation.getAnimatedFraction();
   1019             int propertyMask = propertyBundle.mPropertyMask;
   1020             if ((propertyMask & TRANSFORM_MASK) != 0) {
   1021                 mView.invalidateViewProperty(false, false);
   1022             }
   1023             ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
   1024             if (valueList != null) {
   1025                 int count = valueList.size();
   1026                 for (int i = 0; i < count; ++i) {
   1027                     NameValuesHolder values = valueList.get(i);
   1028                     float value = values.mFromValue + fraction * values.mDeltaValue;
   1029                     if (values.mNameConstant == ALPHA) {
   1030                         alphaHandled = mView.setAlphaNoInvalidation(value);
   1031                     } else {
   1032                         setValue(values.mNameConstant, value);
   1033                     }
   1034                 }
   1035             }
   1036             if ((propertyMask & TRANSFORM_MASK) != 0) {
   1037                 mView.mTransformationInfo.mMatrixDirty = true;
   1038                 if (!useDisplayListProperties) {
   1039                     mView.mPrivateFlags |= View.PFLAG_DRAWN; // force another invalidation
   1040                 }
   1041             }
   1042             // invalidate(false) in all cases except if alphaHandled gets set to true
   1043             // via the call to setAlphaNoInvalidation(), above
   1044             if (alphaHandled) {
   1045                 mView.invalidate(true);
   1046             } else {
   1047                 mView.invalidateViewProperty(false, false);
   1048             }
   1049         }
   1050     }
   1051 }
   1052