• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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