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