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