blob: 14d3d19851f1211d7fe75610e6eaff0207eff19f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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;
Chet Haasec861bd72011-09-06 07:12:44 -070021import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.util.AttributeSet;
23import android.graphics.RectF;
24
25import java.util.ArrayList;
26import java.util.List;
27
28/**
29 * Represents a group of Animations that should be played together.
30 * The transformation of each individual animation are composed
31 * together into a single transform.
32 * If AnimationSet sets any properties that its children also set
33 * (for example, duration or fillBefore), the values of AnimationSet
34 * override the child values.
Chet Haasec861bd72011-09-06 07:12:44 -070035 *
36 * <p>The way that AnimationSet inherits behavior from Animation is important to
37 * understand. Some of the Animation attributes applied to AnimationSet affect the
38 * AnimationSet itself, some are pushed down to the children, and some are ignored,
39 * as follows:
40 * <ul>
41 * <li>duration, repeatMode, fillBefore, fillAfter: These properties, when set
42 * on an AnimationSet object, will be pushed down to all child animations.</li>
43 * <li>repeatCount, fillEnabled: These properties are ignored for AnimationSet.</li>
44 * <li>startOffset, shareInterpolator: These properties apply to the AnimationSet itself.</li>
45 * </ul>
46 * Starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH},
47 * the behavior of these properties is the same in XML resources and at runtime (prior to that
48 * release, the values set in XML were ignored for AnimationSet). That is, calling
49 * <code>setDuration(500)</code> on an AnimationSet has the same effect as declaring
50 * <code>android:duration="500"</code> in an XML resource for an AnimationSet object.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 */
52public class AnimationSet extends Animation {
53 private static final int PROPERTY_FILL_AFTER_MASK = 0x1;
54 private static final int PROPERTY_FILL_BEFORE_MASK = 0x2;
55 private static final int PROPERTY_REPEAT_MODE_MASK = 0x4;
56 private static final int PROPERTY_START_OFFSET_MASK = 0x8;
57 private static final int PROPERTY_SHARE_INTERPOLATOR_MASK = 0x10;
58 private static final int PROPERTY_DURATION_MASK = 0x20;
59 private static final int PROPERTY_MORPH_MATRIX_MASK = 0x40;
60 private static final int PROPERTY_CHANGE_BOUNDS_MASK = 0x80;
61
62 private int mFlags = 0;
Romain Guyd4745a62011-07-22 16:03:07 -070063 private boolean mDirty;
64 private boolean mHasAlpha;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
66 private ArrayList<Animation> mAnimations = new ArrayList<Animation>();
67
68 private Transformation mTempTransformation = new Transformation();
69
70 private long mLastEnd;
71
72 private long[] mStoredOffsets;
73
74 /**
The Android Open Source Projectbdbdc4f2009-03-03 21:00:54 -080075 * Constructor used when an AnimationSet is loaded from a resource.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 *
77 * @param context Application context to use
78 * @param attrs Attribute set from which to read values
79 */
80 public AnimationSet(Context context, AttributeSet attrs) {
81 super(context, attrs);
82
83 TypedArray a =
84 context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AnimationSet);
85
86 setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK,
87 a.getBoolean(com.android.internal.R.styleable.AnimationSet_shareInterpolator, true));
88 init();
Chet Haasec861bd72011-09-06 07:12:44 -070089
90 if (context.getApplicationInfo().targetSdkVersion >=
91 Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Dianne Hackborn89620282011-09-11 12:47:45 -070092 if (a.hasValue(com.android.internal.R.styleable.AnimationSet_duration)) {
Chet Haasec861bd72011-09-06 07:12:44 -070093 mFlags |= PROPERTY_DURATION_MASK;
94 }
Dianne Hackborn89620282011-09-11 12:47:45 -070095 if (a.hasValue(com.android.internal.R.styleable.AnimationSet_fillBefore)) {
Chet Haasec861bd72011-09-06 07:12:44 -070096 mFlags |= PROPERTY_FILL_BEFORE_MASK;
97 }
Dianne Hackborn89620282011-09-11 12:47:45 -070098 if (a.hasValue(com.android.internal.R.styleable.AnimationSet_fillAfter)) {
Chet Haasec861bd72011-09-06 07:12:44 -070099 mFlags |= PROPERTY_FILL_AFTER_MASK;
100 }
Dianne Hackborn89620282011-09-11 12:47:45 -0700101 if (a.hasValue(com.android.internal.R.styleable.AnimationSet_repeatMode)) {
Chet Haasec861bd72011-09-06 07:12:44 -0700102 mFlags |= PROPERTY_REPEAT_MODE_MASK;
103 }
Dianne Hackborn89620282011-09-11 12:47:45 -0700104 if (a.hasValue(com.android.internal.R.styleable.AnimationSet_startOffset)) {
Chet Haasec861bd72011-09-06 07:12:44 -0700105 mFlags |= PROPERTY_START_OFFSET_MASK;
106 }
107 }
108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 a.recycle();
110 }
111
112
113 /**
114 * Constructor to use when building an AnimationSet from code
115 *
116 * @param shareInterpolator Pass true if all of the animations in this set
117 * should use the interpolator assocciated with this AnimationSet.
118 * Pass false if each animation should use its own interpolator.
119 */
120 public AnimationSet(boolean shareInterpolator) {
121 setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK, shareInterpolator);
122 init();
123 }
124
125 @Override
126 protected AnimationSet clone() throws CloneNotSupportedException {
127 final AnimationSet animation = (AnimationSet) super.clone();
128 animation.mTempTransformation = new Transformation();
129 animation.mAnimations = new ArrayList<Animation>();
130
131 final int count = mAnimations.size();
132 final ArrayList<Animation> animations = mAnimations;
133
134 for (int i = 0; i < count; i++) {
135 animation.mAnimations.add(animations.get(i).clone());
136 }
137
138 return animation;
139 }
140
141 private void setFlag(int mask, boolean value) {
142 if (value) {
143 mFlags |= mask;
144 } else {
145 mFlags &= ~mask;
146 }
147 }
148
149 private void init() {
150 mStartTime = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 }
152
153 @Override
154 public void setFillAfter(boolean fillAfter) {
155 mFlags |= PROPERTY_FILL_AFTER_MASK;
156 super.setFillAfter(fillAfter);
157 }
158
159 @Override
160 public void setFillBefore(boolean fillBefore) {
161 mFlags |= PROPERTY_FILL_BEFORE_MASK;
162 super.setFillBefore(fillBefore);
163 }
164
165 @Override
166 public void setRepeatMode(int repeatMode) {
167 mFlags |= PROPERTY_REPEAT_MODE_MASK;
168 super.setRepeatMode(repeatMode);
169 }
170
171 @Override
172 public void setStartOffset(long startOffset) {
173 mFlags |= PROPERTY_START_OFFSET_MASK;
174 super.setStartOffset(startOffset);
175 }
176
Romain Guyc9bcebe2011-07-22 16:46:34 -0700177 /**
178 * @hide
179 */
Romain Guyd4745a62011-07-22 16:03:07 -0700180 @Override
181 public boolean hasAlpha() {
182 if (mDirty) {
183 mDirty = mHasAlpha = false;
184
185 final int count = mAnimations.size();
186 final ArrayList<Animation> animations = mAnimations;
187
188 for (int i = 0; i < count; i++) {
189 if (animations.get(i).hasAlpha()) {
190 mHasAlpha = true;
191 break;
192 }
193 }
194 }
195
196 return mHasAlpha;
197 }
198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 /**
200 * <p>Sets the duration of every child animation.</p>
201 *
202 * @param durationMillis the duration of the animation, in milliseconds, for
203 * every child in this set
204 */
205 @Override
206 public void setDuration(long durationMillis) {
207 mFlags |= PROPERTY_DURATION_MASK;
208 super.setDuration(durationMillis);
Chet Haasec861bd72011-09-06 07:12:44 -0700209 mLastEnd = mStartOffset + mDuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 }
211
212 /**
213 * Add a child animation to this animation set.
214 * The transforms of the child animations are applied in the order
215 * that they were added
216 * @param a Animation to add.
217 */
218 public void addAnimation(Animation a) {
219 mAnimations.add(a);
220
221 boolean noMatrix = (mFlags & PROPERTY_MORPH_MATRIX_MASK) == 0;
222 if (noMatrix && a.willChangeTransformationMatrix()) {
223 mFlags |= PROPERTY_MORPH_MATRIX_MASK;
224 }
225
226 boolean changeBounds = (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == 0;
Chet Haase2d46fcc2011-12-19 18:01:05 -0800227
228
229 if (changeBounds && a.willChangeBounds()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 mFlags |= PROPERTY_CHANGE_BOUNDS_MASK;
231 }
232
Chet Haasec861bd72011-09-06 07:12:44 -0700233 if ((mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 mLastEnd = mStartOffset + mDuration;
235 } else {
Chet Haasec861bd72011-09-06 07:12:44 -0700236 if (mAnimations.size() == 1) {
237 mDuration = a.getStartOffset() + a.getDuration();
238 mLastEnd = mStartOffset + mDuration;
239 } else {
240 mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration());
241 mDuration = mLastEnd - mStartOffset;
242 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 }
Romain Guyd4745a62011-07-22 16:03:07 -0700244
245 mDirty = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 }
247
248 /**
249 * Sets the start time of this animation and all child animations
250 *
251 * @see android.view.animation.Animation#setStartTime(long)
252 */
253 @Override
254 public void setStartTime(long startTimeMillis) {
255 super.setStartTime(startTimeMillis);
256
257 final int count = mAnimations.size();
258 final ArrayList<Animation> animations = mAnimations;
259
260 for (int i = 0; i < count; i++) {
261 Animation a = animations.get(i);
262 a.setStartTime(startTimeMillis);
263 }
264 }
265
266 @Override
267 public long getStartTime() {
268 long startTime = Long.MAX_VALUE;
269
270 final int count = mAnimations.size();
271 final ArrayList<Animation> animations = mAnimations;
272
273 for (int i = 0; i < count; i++) {
274 Animation a = animations.get(i);
275 startTime = Math.min(startTime, a.getStartTime());
276 }
277
278 return startTime;
279 }
280
281 @Override
282 public void restrictDuration(long durationMillis) {
283 super.restrictDuration(durationMillis);
284
285 final ArrayList<Animation> animations = mAnimations;
286 int count = animations.size();
287
288 for (int i = 0; i < count; i++) {
289 animations.get(i).restrictDuration(durationMillis);
290 }
291 }
292
293 /**
294 * The duration of an AnimationSet is defined to be the
295 * duration of the longest child animation.
296 *
297 * @see android.view.animation.Animation#getDuration()
298 */
299 @Override
300 public long getDuration() {
301 final ArrayList<Animation> animations = mAnimations;
302 final int count = animations.size();
303 long duration = 0;
304
305 boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK;
306 if (durationSet) {
307 duration = mDuration;
308 } else {
309 for (int i = 0; i < count; i++) {
310 duration = Math.max(duration, animations.get(i).getDuration());
311 }
312 }
313
314 return duration;
315 }
316
317 /**
318 * The duration hint of an animation set is the maximum of the duration
319 * hints of all of its component animations.
320 *
321 * @see android.view.animation.Animation#computeDurationHint
322 */
323 public long computeDurationHint() {
324 long duration = 0;
325 final int count = mAnimations.size();
326 final ArrayList<Animation> animations = mAnimations;
327 for (int i = count - 1; i >= 0; --i) {
328 final long d = animations.get(i).computeDurationHint();
329 if (d > duration) duration = d;
330 }
331 return duration;
332 }
333
334 /**
335 * @hide
336 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
338 final RectF region = mPreviousRegion;
339 region.set(left, top, right, bottom);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800340 region.inset(-1.0f, -1.0f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341
342 if (mFillBefore) {
343 final int count = mAnimations.size();
344 final ArrayList<Animation> animations = mAnimations;
345 final Transformation temp = mTempTransformation;
346
347 final Transformation previousTransformation = mPreviousTransformation;
348
349 for (int i = count - 1; i >= 0; --i) {
350 final Animation a = animations.get(i);
Chet Haase00418612011-12-20 15:56:15 -0800351 if (!a.isFillEnabled() || a.getFillBefore() || a.getStartOffset() == 0) {
352 temp.clear();
353 final Interpolator interpolator = a.mInterpolator;
354 a.applyTransformation(interpolator != null ? interpolator.getInterpolation(0.0f)
355 : 0.0f, temp);
356 previousTransformation.compose(temp);
357 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 }
359 }
360 }
361
362 /**
363 * The transformation of an animation set is the concatenation of all of its
364 * component animations.
365 *
366 * @see android.view.animation.Animation#getTransformation
367 */
368 @Override
369 public boolean getTransformation(long currentTime, Transformation t) {
370 final int count = mAnimations.size();
371 final ArrayList<Animation> animations = mAnimations;
372 final Transformation temp = mTempTransformation;
373
374 boolean more = false;
375 boolean started = false;
376 boolean ended = true;
377
378 t.clear();
379
380 for (int i = count - 1; i >= 0; --i) {
381 final Animation a = animations.get(i);
382
383 temp.clear();
Chet Haase48460322010-06-11 14:22:25 -0700384 more = a.getTransformation(currentTime, temp, getScaleFactor()) || more;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 t.compose(temp);
386
387 started = started || a.hasStarted();
388 ended = a.hasEnded() && ended;
389 }
390
391 if (started && !mStarted) {
392 if (mListener != null) {
393 mListener.onAnimationStart(this);
394 }
395 mStarted = true;
396 }
397
398 if (ended != mEnded) {
399 if (mListener != null) {
400 mListener.onAnimationEnd(this);
401 }
402 mEnded = ended;
403 }
404
405 return more;
406 }
407
408 /**
409 * @see android.view.animation.Animation#scaleCurrentDuration(float)
410 */
411 @Override
412 public void scaleCurrentDuration(float scale) {
413 final ArrayList<Animation> animations = mAnimations;
414 int count = animations.size();
415 for (int i = 0; i < count; i++) {
416 animations.get(i).scaleCurrentDuration(scale);
417 }
418 }
419
420 /**
421 * @see android.view.animation.Animation#initialize(int, int, int, int)
422 */
423 @Override
424 public void initialize(int width, int height, int parentWidth, int parentHeight) {
425 super.initialize(width, height, parentWidth, parentHeight);
426
427 boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK;
428 boolean fillAfterSet = (mFlags & PROPERTY_FILL_AFTER_MASK) == PROPERTY_FILL_AFTER_MASK;
429 boolean fillBeforeSet = (mFlags & PROPERTY_FILL_BEFORE_MASK) == PROPERTY_FILL_BEFORE_MASK;
430 boolean repeatModeSet = (mFlags & PROPERTY_REPEAT_MODE_MASK) == PROPERTY_REPEAT_MODE_MASK;
431 boolean shareInterpolator = (mFlags & PROPERTY_SHARE_INTERPOLATOR_MASK)
432 == PROPERTY_SHARE_INTERPOLATOR_MASK;
433 boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK)
434 == PROPERTY_START_OFFSET_MASK;
435
436 if (shareInterpolator) {
437 ensureInterpolator();
438 }
439
440 final ArrayList<Animation> children = mAnimations;
441 final int count = children.size();
442
443 final long duration = mDuration;
444 final boolean fillAfter = mFillAfter;
445 final boolean fillBefore = mFillBefore;
446 final int repeatMode = mRepeatMode;
447 final Interpolator interpolator = mInterpolator;
448 final long startOffset = mStartOffset;
449
450
451 long[] storedOffsets = mStoredOffsets;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800452 if (startOffsetSet) {
453 if (storedOffsets == null || storedOffsets.length != count) {
454 storedOffsets = mStoredOffsets = new long[count];
455 }
456 } else if (storedOffsets != null) {
457 storedOffsets = mStoredOffsets = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 }
459
460 for (int i = 0; i < count; i++) {
461 Animation a = children.get(i);
462 if (durationSet) {
463 a.setDuration(duration);
464 }
465 if (fillAfterSet) {
466 a.setFillAfter(fillAfter);
467 }
468 if (fillBeforeSet) {
469 a.setFillBefore(fillBefore);
470 }
471 if (repeatModeSet) {
472 a.setRepeatMode(repeatMode);
473 }
474 if (shareInterpolator) {
475 a.setInterpolator(interpolator);
476 }
477 if (startOffsetSet) {
478 long offset = a.getStartOffset();
479 a.setStartOffset(offset + startOffset);
480 storedOffsets[i] = offset;
481 }
482 a.initialize(width, height, parentWidth, parentHeight);
483 }
484 }
485
486 @Override
487 public void reset() {
488 super.reset();
489 restoreChildrenStartOffset();
490 }
491
492 /**
493 * @hide
494 */
495 void restoreChildrenStartOffset() {
496 final long[] offsets = mStoredOffsets;
497 if (offsets == null) return;
498
499 final ArrayList<Animation> children = mAnimations;
500 final int count = children.size();
501
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 for (int i = 0; i < count; i++) {
503 children.get(i).setStartOffset(offsets[i]);
504 }
505 }
506
507 /**
508 * @return All the child animations in this AnimationSet. Note that
509 * this may include other AnimationSets, which are not expanded.
510 */
511 public List<Animation> getAnimations() {
512 return mAnimations;
513 }
514
515 @Override
516 public boolean willChangeTransformationMatrix() {
517 return (mFlags & PROPERTY_MORPH_MATRIX_MASK) == PROPERTY_MORPH_MATRIX_MASK;
518 }
519
520 @Override
521 public boolean willChangeBounds() {
522 return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK;
523 }
524}