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 17package android.view.animation; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.util.AttributeSet; 22import android.view.View; 23import android.view.ViewGroup; 24 25import 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 */ 54public 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