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