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