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      */
LayoutAnimationController(Context context, AttributeSet attrs)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      */
LayoutAnimationController(Animation animation)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      */
LayoutAnimationController(Animation animation, float delay)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      */
getOrder()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      */
setOrder(int order)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      */
setAnimation(Context context, @AnimRes int resourceID)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      */
setAnimation(Animation animation)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      */
getAnimation()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      */
setInterpolator(Context context, @InterpolatorRes int resourceID)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      */
setInterpolator(Interpolator interpolator)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      */
getInterpolator()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      */
getDelay()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      */
setDelay(float delay)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      */
willOverlap()293     public boolean willOverlap() {
294         return mDelay < 1.0f;
295     }
296 
297     /**
298      * Starts the animation.
299      */
start()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      */
getAnimationForView(View view)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      */
isDone()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      */
getDelayForView(View view)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      */
getTransformedIndex(AnimationParameters params)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