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