Home | History | Annotate | Download | only in animation
      1 /*
      2  * Copyright (C) 2007 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.animation;
     18 
     19 import android.annotation.AnimRes;
     20 import android.annotation.InterpolatorRes;
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.util.AttributeSet;
     24 import android.view.View;
     25 import android.view.ViewGroup;
     26 
     27 import java.util.Random;
     28 
     29 /**
     30  * A layout animation controller is used to animated a layout's, or a view
     31  * group's, children. Each child uses the same animation but for every one of
     32  * them, the animation starts at a different time. A layout animation controller
     33  * is used by {@link android.view.ViewGroup} to compute the delay by which each
     34  * child's animation start must be offset. The delay is computed by using
     35  * characteristics of each child, like its index in the view group.
     36  *
     37  * This standard implementation computes the delay by multiplying a fixed
     38  * amount of miliseconds by the index of the child in its parent view group.
     39  * Subclasses are supposed to override
     40  * {@link #getDelayForView(android.view.View)} to implement a different way
     41  * of computing the delay. For instance, a
     42  * {@link android.view.animation.GridLayoutAnimationController} will compute the
     43  * delay based on the column and row indices of the child in its parent view
     44  * group.
     45  *
     46  * Information used to compute the animation delay of each child are stored
     47  * in an instance of
     48  * {@link android.view.animation.LayoutAnimationController.AnimationParameters},
     49  * itself stored in the {@link android.view.ViewGroup.LayoutParams} of the view.
     50  *
     51  * @attr ref android.R.styleable#LayoutAnimation_delay
     52  * @attr ref android.R.styleable#LayoutAnimation_animationOrder
     53  * @attr ref android.R.styleable#LayoutAnimation_interpolator
     54  * @attr ref android.R.styleable#LayoutAnimation_animation
     55  */
     56 public class LayoutAnimationController {
     57     /**
     58      * Distributes the animation delays in the order in which view were added
     59      * to their view group.
     60      */
     61     public static final int ORDER_NORMAL  = 0;
     62 
     63     /**
     64      * Distributes the animation delays in the reverse order in which view were
     65      * added to their view group.
     66      */
     67     public static final int ORDER_REVERSE = 1;
     68 
     69     /**
     70      * Randomly distributes the animation delays.
     71      */
     72     public static final int ORDER_RANDOM  = 2;
     73 
     74     /**
     75      * The animation applied on each child of the view group on which this
     76      * layout animation controller is set.
     77      */
     78     protected Animation mAnimation;
     79 
     80     /**
     81      * The randomizer used when the order is set to random. Subclasses should
     82      * use this object to avoid creating their own.
     83      */
     84     protected Random mRandomizer;
     85 
     86     /**
     87      * The interpolator used to interpolate the delays.
     88      */
     89     protected Interpolator mInterpolator;
     90 
     91     private float mDelay;
     92     private int mOrder;
     93 
     94     private long mDuration;
     95     private long mMaxDelay;
     96 
     97     /**
     98      * Creates a new layout animation controller from external resources.
     99      *
    100      * @param context the Context the view  group is running in, through which
    101      *        it can access the resources
    102      * @param attrs the attributes of the XML tag that is inflating the
    103      *        layout animation controller
    104      */
    105     public LayoutAnimationController(Context context, AttributeSet attrs) {
    106         TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LayoutAnimation);
    107 
    108         Animation.Description d = Animation.Description.parseValue(
    109                 a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay));
    110         mDelay = d.value;
    111 
    112         mOrder = a.getInt(com.android.internal.R.styleable.LayoutAnimation_animationOrder, ORDER_NORMAL);
    113 
    114         int resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_animation, 0);
    115         if (resource > 0) {
    116             setAnimation(context, resource);
    117         }
    118 
    119         resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_interpolator, 0);
    120         if (resource > 0) {
    121             setInterpolator(context, resource);
    122         }
    123 
    124         a.recycle();
    125     }
    126 
    127     /**
    128      * Creates a new layout animation controller with a delay of 50%
    129      * and the specified animation.
    130      *
    131      * @param animation the animation to use on each child of the view group
    132      */
    133     public LayoutAnimationController(Animation animation) {
    134         this(animation, 0.5f);
    135     }
    136 
    137     /**
    138      * Creates a new layout animation controller with the specified delay
    139      * and the specified animation.
    140      *
    141      * @param animation the animation to use on each child of the view group
    142      * @param delay the delay by which each child's animation must be offset
    143      */
    144     public LayoutAnimationController(Animation animation, float delay) {
    145         mDelay = delay;
    146         setAnimation(animation);
    147     }
    148 
    149     /**
    150      * Returns the order used to compute the delay of each child's animation.
    151      *
    152      * @return one of {@link #ORDER_NORMAL}, {@link #ORDER_REVERSE} or
    153      *         {@link #ORDER_RANDOM)
    154      *
    155      * @attr ref android.R.styleable#LayoutAnimation_animationOrder
    156      */
    157     public int getOrder() {
    158         return mOrder;
    159     }
    160 
    161     /**
    162      * Sets the order used to compute the delay of each child's animation.
    163      *
    164      * @param order one of {@link #ORDER_NORMAL}, {@link #ORDER_REVERSE} or
    165      *        {@link #ORDER_RANDOM}
    166      *
    167      * @attr ref android.R.styleable#LayoutAnimation_animationOrder
    168      */
    169     public void setOrder(int order) {
    170         mOrder = order;
    171     }
    172 
    173     /**
    174      * Sets the animation to be run on each child of the view group on which
    175      * this layout animation controller is .
    176      *
    177      * @param context the context from which the animation must be inflated
    178      * @param resourceID the resource identifier of the animation
    179      *
    180      * @see #setAnimation(Animation)
    181      * @see #getAnimation()
    182      *
    183      * @attr ref android.R.styleable#LayoutAnimation_animation
    184      */
    185     public void setAnimation(Context context, @AnimRes int resourceID) {
    186         setAnimation(AnimationUtils.loadAnimation(context, resourceID));
    187     }
    188 
    189     /**
    190      * Sets the animation to be run on each child of the view group on which
    191      * this layout animation controller is .
    192      *
    193      * @param animation the animation to run on each child of the view group
    194 
    195      * @see #setAnimation(android.content.Context, int)
    196      * @see #getAnimation()
    197      *
    198      * @attr ref android.R.styleable#LayoutAnimation_animation
    199      */
    200     public void setAnimation(Animation animation) {
    201         mAnimation = animation;
    202         mAnimation.setFillBefore(true);
    203     }
    204 
    205     /**
    206      * Returns the animation applied to each child of the view group on which
    207      * this controller is set.
    208      *
    209      * @return an {@link android.view.animation.Animation} instance
    210      *
    211      * @see #setAnimation(android.content.Context, int)
    212      * @see #setAnimation(Animation)
    213      */
    214     public Animation getAnimation() {
    215         return mAnimation;
    216     }
    217 
    218     /**
    219      * Sets the interpolator used to interpolate the delays between the
    220      * children.
    221      *
    222      * @param context the context from which the interpolator must be inflated
    223      * @param resourceID the resource identifier of the interpolator
    224      *
    225      * @see #getInterpolator()
    226      * @see #setInterpolator(Interpolator)
    227      *
    228      * @attr ref android.R.styleable#LayoutAnimation_interpolator
    229      */
    230     public void setInterpolator(Context context, @InterpolatorRes int resourceID) {
    231         setInterpolator(AnimationUtils.loadInterpolator(context, resourceID));
    232     }
    233 
    234     /**
    235      * Sets the interpolator used to interpolate the delays between the
    236      * children.
    237      *
    238      * @param interpolator the interpolator
    239      *
    240      * @see #getInterpolator()
    241      * @see #setInterpolator(Interpolator)
    242      *
    243      * @attr ref android.R.styleable#LayoutAnimation_interpolator
    244      */
    245     public void setInterpolator(Interpolator interpolator) {
    246         mInterpolator = interpolator;
    247     }
    248 
    249     /**
    250      * Returns the interpolator used to interpolate the delays between the
    251      * children.
    252      *
    253      * @return an {@link android.view.animation.Interpolator}
    254      */
    255     public Interpolator getInterpolator() {
    256         return mInterpolator;
    257     }
    258 
    259     /**
    260      * Returns the delay by which the children's animation are offset. The
    261      * delay is expressed as a fraction of the animation duration.
    262      *
    263      * @return a fraction of the animation duration
    264      *
    265      * @see #setDelay(float)
    266      */
    267     public float getDelay() {
    268         return mDelay;
    269     }
    270 
    271     /**
    272      * Sets the delay, as a fraction of the animation duration, by which the
    273      * children's animations are offset. The general formula is:
    274      *
    275      * <pre>
    276      * child animation delay = child index * delay * animation duration
    277      * </pre>
    278      *
    279      * @param delay a fraction of the animation duration
    280      *
    281      * @see #getDelay()
    282      */
    283     public void setDelay(float delay) {
    284         mDelay = delay;
    285     }
    286 
    287     /**
    288      * Indicates whether two children's animations will overlap. Animations
    289      * overlap when the delay is lower than 100% (or 1.0).
    290      *
    291      * @return true if animations will overlap, false otherwise
    292      */
    293     public boolean willOverlap() {
    294         return mDelay < 1.0f;
    295     }
    296 
    297     /**
    298      * Starts the animation.
    299      */
    300     public void start() {
    301         mDuration = mAnimation.getDuration();
    302         mMaxDelay = Long.MIN_VALUE;
    303         mAnimation.setStartTime(-1);
    304     }
    305 
    306     /**
    307      * Returns the animation to be applied to the specified view. The returned
    308      * animation is delayed by an offset computed according to the information
    309      * provided by
    310      * {@link android.view.animation.LayoutAnimationController.AnimationParameters}.
    311      * This method is called by view groups to obtain the animation to set on
    312      * a specific child.
    313      *
    314      * @param view the view to animate
    315      * @return an animation delayed by the number of milliseconds returned by
    316      *         {@link #getDelayForView(android.view.View)}
    317      *
    318      * @see #getDelay()
    319      * @see #setDelay(float)
    320      * @see #getDelayForView(android.view.View)
    321      */
    322     public final Animation getAnimationForView(View view) {
    323         final long delay = getDelayForView(view) + mAnimation.getStartOffset();
    324         mMaxDelay = Math.max(mMaxDelay, delay);
    325 
    326         try {
    327             final Animation animation = mAnimation.clone();
    328             animation.setStartOffset(delay);
    329             return animation;
    330         } catch (CloneNotSupportedException e) {
    331             return null;
    332         }
    333     }
    334 
    335     /**
    336      * Indicates whether the layout animation is over or not. A layout animation
    337      * is considered done when the animation with the longest delay is done.
    338      *
    339      * @return true if all of the children's animations are over, false otherwise
    340      */
    341     public boolean isDone() {
    342         return AnimationUtils.currentAnimationTimeMillis() >
    343                 mAnimation.getStartTime() + mMaxDelay + mDuration;
    344     }
    345 
    346     /**
    347      * Returns the amount of milliseconds by which the specified view's
    348      * animation must be delayed or offset. Subclasses should override this
    349      * method to return a suitable value.
    350      *
    351      * This implementation returns <code>child animation delay</code>
    352      * milliseconds where:
    353      *
    354      * <pre>
    355      * child animation delay = child index * delay
    356      * </pre>
    357      *
    358      * The index is retrieved from the
    359      * {@link android.view.animation.LayoutAnimationController.AnimationParameters}
    360      * found in the view's {@link android.view.ViewGroup.LayoutParams}.
    361      *
    362      * @param view the view for which to obtain the animation's delay
    363      * @return a delay in milliseconds
    364      *
    365      * @see #getAnimationForView(android.view.View)
    366      * @see #getDelay()
    367      * @see #getTransformedIndex(android.view.animation.LayoutAnimationController.AnimationParameters)
    368      * @see android.view.ViewGroup.LayoutParams
    369      */
    370     protected long getDelayForView(View view) {
    371         ViewGroup.LayoutParams lp = view.getLayoutParams();
    372         AnimationParameters params = lp.layoutAnimationParameters;
    373 
    374         if (params == null) {
    375             return 0;
    376         }
    377 
    378         final float delay = mDelay * mAnimation.getDuration();
    379         final long viewDelay = (long) (getTransformedIndex(params) * delay);
    380         final float totalDelay = delay * params.count;
    381 
    382         if (mInterpolator == null) {
    383             mInterpolator = new LinearInterpolator();
    384         }
    385 
    386         float normalizedDelay = viewDelay / totalDelay;
    387         normalizedDelay = mInterpolator.getInterpolation(normalizedDelay);
    388 
    389         return (long) (normalizedDelay * totalDelay);
    390     }
    391 
    392     /**
    393      * Transforms the index stored in
    394      * {@link android.view.animation.LayoutAnimationController.AnimationParameters}
    395      * by the order returned by {@link #getOrder()}. Subclasses should override
    396      * this method to provide additional support for other types of ordering.
    397      * This method should be invoked by
    398      * {@link #getDelayForView(android.view.View)} prior to any computation.
    399      *
    400      * @param params the animation parameters containing the index
    401      * @return a transformed index
    402      */
    403     protected int getTransformedIndex(AnimationParameters params) {
    404         switch (getOrder()) {
    405             case ORDER_REVERSE:
    406                 return params.count - 1 - params.index;
    407             case ORDER_RANDOM:
    408                 if (mRandomizer == null) {
    409                     mRandomizer = new Random();
    410                 }
    411                 return (int) (params.count * mRandomizer.nextFloat());
    412             case ORDER_NORMAL:
    413             default:
    414                 return params.index;
    415         }
    416     }
    417 
    418     /**
    419      * The set of parameters that has to be attached to each view contained in
    420      * the view group animated by the layout animation controller. These
    421      * parameters are used to compute the start time of each individual view's
    422      * animation.
    423      */
    424     public static class AnimationParameters {
    425         /**
    426          * The number of children in the view group containing the view to which
    427          * these parameters are attached.
    428          */
    429         public int count;
    430 
    431         /**
    432          * The index of the view to which these parameters are attached in its
    433          * containing view group.
    434          */
    435         public int index;
    436     }
    437 }
    438