AnimationSet.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1/* 2 * Copyright (C) 2006 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.graphics.RectF; 23 24import java.util.ArrayList; 25import java.util.List; 26 27/** 28 * Represents a group of Animations that should be played together. 29 * The transformation of each individual animation are composed 30 * together into a single transform. 31 * If AnimationSet sets any properties that its children also set 32 * (for example, duration or fillBefore), the values of AnimationSet 33 * override the child values. 34 */ 35public class AnimationSet extends Animation { 36 private static final int PROPERTY_FILL_AFTER_MASK = 0x1; 37 private static final int PROPERTY_FILL_BEFORE_MASK = 0x2; 38 private static final int PROPERTY_REPEAT_MODE_MASK = 0x4; 39 private static final int PROPERTY_START_OFFSET_MASK = 0x8; 40 private static final int PROPERTY_SHARE_INTERPOLATOR_MASK = 0x10; 41 private static final int PROPERTY_DURATION_MASK = 0x20; 42 private static final int PROPERTY_MORPH_MATRIX_MASK = 0x40; 43 private static final int PROPERTY_CHANGE_BOUNDS_MASK = 0x80; 44 45 private int mFlags = 0; 46 47 private ArrayList<Animation> mAnimations = new ArrayList<Animation>(); 48 49 private Transformation mTempTransformation = new Transformation(); 50 51 private long mLastEnd; 52 53 private long[] mStoredOffsets; 54 55 /** 56 * Constructor used whan an AnimationSet is loaded from a resource. 57 * 58 * @param context Application context to use 59 * @param attrs Attribute set from which to read values 60 */ 61 public AnimationSet(Context context, AttributeSet attrs) { 62 super(context, attrs); 63 64 TypedArray a = 65 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AnimationSet); 66 67 setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK, 68 a.getBoolean(com.android.internal.R.styleable.AnimationSet_shareInterpolator, true)); 69 init(); 70 71 a.recycle(); 72 } 73 74 75 /** 76 * Constructor to use when building an AnimationSet from code 77 * 78 * @param shareInterpolator Pass true if all of the animations in this set 79 * should use the interpolator assocciated with this AnimationSet. 80 * Pass false if each animation should use its own interpolator. 81 */ 82 public AnimationSet(boolean shareInterpolator) { 83 setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK, shareInterpolator); 84 init(); 85 } 86 87 @Override 88 protected AnimationSet clone() throws CloneNotSupportedException { 89 final AnimationSet animation = (AnimationSet) super.clone(); 90 animation.mTempTransformation = new Transformation(); 91 animation.mAnimations = new ArrayList<Animation>(); 92 93 final int count = mAnimations.size(); 94 final ArrayList<Animation> animations = mAnimations; 95 96 for (int i = 0; i < count; i++) { 97 animation.mAnimations.add(animations.get(i).clone()); 98 } 99 100 return animation; 101 } 102 103 private void setFlag(int mask, boolean value) { 104 if (value) { 105 mFlags |= mask; 106 } else { 107 mFlags &= ~mask; 108 } 109 } 110 111 private void init() { 112 mStartTime = 0; 113 mDuration = 0; 114 } 115 116 @Override 117 public void setFillAfter(boolean fillAfter) { 118 mFlags |= PROPERTY_FILL_AFTER_MASK; 119 super.setFillAfter(fillAfter); 120 } 121 122 @Override 123 public void setFillBefore(boolean fillBefore) { 124 mFlags |= PROPERTY_FILL_BEFORE_MASK; 125 super.setFillBefore(fillBefore); 126 } 127 128 @Override 129 public void setRepeatMode(int repeatMode) { 130 mFlags |= PROPERTY_REPEAT_MODE_MASK; 131 super.setRepeatMode(repeatMode); 132 } 133 134 @Override 135 public void setStartOffset(long startOffset) { 136 mFlags |= PROPERTY_START_OFFSET_MASK; 137 super.setStartOffset(startOffset); 138 } 139 140 /** 141 * <p>Sets the duration of every child animation.</p> 142 * 143 * @param durationMillis the duration of the animation, in milliseconds, for 144 * every child in this set 145 */ 146 @Override 147 public void setDuration(long durationMillis) { 148 mFlags |= PROPERTY_DURATION_MASK; 149 super.setDuration(durationMillis); 150 } 151 152 /** 153 * Add a child animation to this animation set. 154 * The transforms of the child animations are applied in the order 155 * that they were added 156 * @param a Animation to add. 157 */ 158 public void addAnimation(Animation a) { 159 mAnimations.add(a); 160 161 boolean noMatrix = (mFlags & PROPERTY_MORPH_MATRIX_MASK) == 0; 162 if (noMatrix && a.willChangeTransformationMatrix()) { 163 mFlags |= PROPERTY_MORPH_MATRIX_MASK; 164 } 165 166 boolean changeBounds = (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == 0; 167 if (changeBounds && a.willChangeTransformationMatrix()) { 168 mFlags |= PROPERTY_CHANGE_BOUNDS_MASK; 169 } 170 171 if (mAnimations.size() == 1) { 172 mDuration = a.getStartOffset() + a.getDuration(); 173 mLastEnd = mStartOffset + mDuration; 174 } else { 175 mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration()); 176 mDuration = mLastEnd - mStartOffset; 177 } 178 } 179 180 /** 181 * Sets the start time of this animation and all child animations 182 * 183 * @see android.view.animation.Animation#setStartTime(long) 184 */ 185 @Override 186 public void setStartTime(long startTimeMillis) { 187 super.setStartTime(startTimeMillis); 188 189 final int count = mAnimations.size(); 190 final ArrayList<Animation> animations = mAnimations; 191 192 for (int i = 0; i < count; i++) { 193 Animation a = animations.get(i); 194 a.setStartTime(startTimeMillis); 195 } 196 } 197 198 @Override 199 public long getStartTime() { 200 long startTime = Long.MAX_VALUE; 201 202 final int count = mAnimations.size(); 203 final ArrayList<Animation> animations = mAnimations; 204 205 for (int i = 0; i < count; i++) { 206 Animation a = animations.get(i); 207 startTime = Math.min(startTime, a.getStartTime()); 208 } 209 210 return startTime; 211 } 212 213 @Override 214 public void restrictDuration(long durationMillis) { 215 super.restrictDuration(durationMillis); 216 217 final ArrayList<Animation> animations = mAnimations; 218 int count = animations.size(); 219 220 for (int i = 0; i < count; i++) { 221 animations.get(i).restrictDuration(durationMillis); 222 } 223 } 224 225 /** 226 * The duration of an AnimationSet is defined to be the 227 * duration of the longest child animation. 228 * 229 * @see android.view.animation.Animation#getDuration() 230 */ 231 @Override 232 public long getDuration() { 233 final ArrayList<Animation> animations = mAnimations; 234 final int count = animations.size(); 235 long duration = 0; 236 237 boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK; 238 if (durationSet) { 239 duration = mDuration; 240 } else { 241 for (int i = 0; i < count; i++) { 242 duration = Math.max(duration, animations.get(i).getDuration()); 243 } 244 } 245 246 return duration; 247 } 248 249 /** 250 * The duration hint of an animation set is the maximum of the duration 251 * hints of all of its component animations. 252 * 253 * @see android.view.animation.Animation#computeDurationHint 254 */ 255 public long computeDurationHint() { 256 long duration = 0; 257 final int count = mAnimations.size(); 258 final ArrayList<Animation> animations = mAnimations; 259 for (int i = count - 1; i >= 0; --i) { 260 final long d = animations.get(i).computeDurationHint(); 261 if (d > duration) duration = d; 262 } 263 return duration; 264 } 265 266 /** 267 * @hide 268 */ 269 public void getInvalidateRegion(int left, int top, int right, int bottom, 270 RectF invalidate, Transformation transformation) { 271 272 final RectF previousRegion = mPreviousRegion; 273 274 invalidate.set(left, top, right, bottom); 275 transformation.getMatrix().mapRect(invalidate); 276 invalidate.union(previousRegion); 277 278 previousRegion.set(left, top, right, bottom); 279 transformation.getMatrix().mapRect(previousRegion); 280 281 final Transformation tempTransformation = mTransformation; 282 final Transformation previousTransformation = mPreviousTransformation; 283 284 tempTransformation.set(transformation); 285 transformation.set(previousTransformation); 286 previousTransformation.set(tempTransformation); 287 } 288 289 /** 290 * @hide 291 */ 292 public void initializeInvalidateRegion(int left, int top, int right, int bottom) { 293 final RectF region = mPreviousRegion; 294 region.set(left, top, right, bottom); 295 296 if (mFillBefore) { 297 final int count = mAnimations.size(); 298 final ArrayList<Animation> animations = mAnimations; 299 final Transformation temp = mTempTransformation; 300 301 final Transformation previousTransformation = mPreviousTransformation; 302 303 for (int i = count - 1; i >= 0; --i) { 304 final Animation a = animations.get(i); 305 306 temp.clear(); 307 a.applyTransformation(0.0f, temp); 308 previousTransformation.compose(temp); 309 } 310 } 311 } 312 313 /** 314 * The transformation of an animation set is the concatenation of all of its 315 * component animations. 316 * 317 * @see android.view.animation.Animation#getTransformation 318 */ 319 @Override 320 public boolean getTransformation(long currentTime, Transformation t) { 321 final int count = mAnimations.size(); 322 final ArrayList<Animation> animations = mAnimations; 323 final Transformation temp = mTempTransformation; 324 325 boolean more = false; 326 boolean started = false; 327 boolean ended = true; 328 329 t.clear(); 330 331 for (int i = count - 1; i >= 0; --i) { 332 final Animation a = animations.get(i); 333 334 temp.clear(); 335 more = a.getTransformation(currentTime, temp) || more; 336 t.compose(temp); 337 338 started = started || a.hasStarted(); 339 ended = a.hasEnded() && ended; 340 } 341 342 if (started && !mStarted) { 343 if (mListener != null) { 344 mListener.onAnimationStart(this); 345 } 346 mStarted = true; 347 } 348 349 if (ended != mEnded) { 350 if (mListener != null) { 351 mListener.onAnimationEnd(this); 352 } 353 mEnded = ended; 354 } 355 356 return more; 357 } 358 359 /** 360 * @see android.view.animation.Animation#scaleCurrentDuration(float) 361 */ 362 @Override 363 public void scaleCurrentDuration(float scale) { 364 final ArrayList<Animation> animations = mAnimations; 365 int count = animations.size(); 366 for (int i = 0; i < count; i++) { 367 animations.get(i).scaleCurrentDuration(scale); 368 } 369 } 370 371 /** 372 * @see android.view.animation.Animation#initialize(int, int, int, int) 373 */ 374 @Override 375 public void initialize(int width, int height, int parentWidth, int parentHeight) { 376 super.initialize(width, height, parentWidth, parentHeight); 377 378 boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK; 379 boolean fillAfterSet = (mFlags & PROPERTY_FILL_AFTER_MASK) == PROPERTY_FILL_AFTER_MASK; 380 boolean fillBeforeSet = (mFlags & PROPERTY_FILL_BEFORE_MASK) == PROPERTY_FILL_BEFORE_MASK; 381 boolean repeatModeSet = (mFlags & PROPERTY_REPEAT_MODE_MASK) == PROPERTY_REPEAT_MODE_MASK; 382 boolean shareInterpolator = (mFlags & PROPERTY_SHARE_INTERPOLATOR_MASK) 383 == PROPERTY_SHARE_INTERPOLATOR_MASK; 384 boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK) 385 == PROPERTY_START_OFFSET_MASK; 386 387 if (shareInterpolator) { 388 ensureInterpolator(); 389 } 390 391 final ArrayList<Animation> children = mAnimations; 392 final int count = children.size(); 393 394 final long duration = mDuration; 395 final boolean fillAfter = mFillAfter; 396 final boolean fillBefore = mFillBefore; 397 final int repeatMode = mRepeatMode; 398 final Interpolator interpolator = mInterpolator; 399 final long startOffset = mStartOffset; 400 401 402 long[] storedOffsets = mStoredOffsets; 403 if (storedOffsets == null || storedOffsets.length != count) { 404 storedOffsets = mStoredOffsets = new long[count]; 405 } 406 407 for (int i = 0; i < count; i++) { 408 Animation a = children.get(i); 409 if (durationSet) { 410 a.setDuration(duration); 411 } 412 if (fillAfterSet) { 413 a.setFillAfter(fillAfter); 414 } 415 if (fillBeforeSet) { 416 a.setFillBefore(fillBefore); 417 } 418 if (repeatModeSet) { 419 a.setRepeatMode(repeatMode); 420 } 421 if (shareInterpolator) { 422 a.setInterpolator(interpolator); 423 } 424 if (startOffsetSet) { 425 long offset = a.getStartOffset(); 426 a.setStartOffset(offset + startOffset); 427 storedOffsets[i] = offset; 428 } 429 a.initialize(width, height, parentWidth, parentHeight); 430 } 431 } 432 433 @Override 434 public void reset() { 435 super.reset(); 436 restoreChildrenStartOffset(); 437 } 438 439 /** 440 * @hide 441 */ 442 void restoreChildrenStartOffset() { 443 final long[] offsets = mStoredOffsets; 444 if (offsets == null) return; 445 446 final ArrayList<Animation> children = mAnimations; 447 final int count = children.size(); 448 449 450 for (int i = 0; i < count; i++) { 451 children.get(i).setStartOffset(offsets[i]); 452 } 453 } 454 455 /** 456 * @return All the child animations in this AnimationSet. Note that 457 * this may include other AnimationSets, which are not expanded. 458 */ 459 public List<Animation> getAnimations() { 460 return mAnimations; 461 } 462 463 @Override 464 public boolean willChangeTransformationMatrix() { 465 return (mFlags & PROPERTY_MORPH_MATRIX_MASK) == PROPERTY_MORPH_MATRIX_MASK; 466 } 467 468 @Override 469 public boolean willChangeBounds() { 470 return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK; 471 } 472} 473