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