• 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.util.AttributeSet;
22 import android.graphics.RectF;
23 
24 import java.util.ArrayList;
25 import 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  */
35 public 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 when 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      */
AnimationSet(Context context, AttributeSet attrs)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      */
AnimationSet(boolean shareInterpolator)82     public AnimationSet(boolean shareInterpolator) {
83         setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK, shareInterpolator);
84         init();
85     }
86 
87     @Override
clone()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 
setFlag(int mask, boolean value)103     private void setFlag(int mask, boolean value) {
104         if (value) {
105             mFlags |= mask;
106         } else {
107             mFlags &= ~mask;
108         }
109     }
110 
init()111     private void init() {
112         mStartTime = 0;
113         mDuration = 0;
114     }
115 
116     @Override
setFillAfter(boolean fillAfter)117     public void setFillAfter(boolean fillAfter) {
118         mFlags |= PROPERTY_FILL_AFTER_MASK;
119         super.setFillAfter(fillAfter);
120     }
121 
122     @Override
setFillBefore(boolean fillBefore)123     public void setFillBefore(boolean fillBefore) {
124         mFlags |= PROPERTY_FILL_BEFORE_MASK;
125         super.setFillBefore(fillBefore);
126     }
127 
128     @Override
setRepeatMode(int repeatMode)129     public void setRepeatMode(int repeatMode) {
130         mFlags |= PROPERTY_REPEAT_MODE_MASK;
131         super.setRepeatMode(repeatMode);
132     }
133 
134     @Override
setStartOffset(long startOffset)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
setDuration(long durationMillis)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      */
addAnimation(Animation a)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
setStartTime(long startTimeMillis)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
getStartTime()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
restrictDuration(long durationMillis)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
getDuration()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      */
computeDurationHint()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      */
initializeInvalidateRegion(int left, int top, int right, int bottom)269     public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
270         final RectF region = mPreviousRegion;
271         region.set(left, top, right, bottom);
272         region.inset(-1.0f, -1.0f);
273 
274         if (mFillBefore) {
275             final int count = mAnimations.size();
276             final ArrayList<Animation> animations = mAnimations;
277             final Transformation temp = mTempTransformation;
278 
279             final Transformation previousTransformation = mPreviousTransformation;
280 
281             for (int i = count - 1; i >= 0; --i) {
282                 final Animation a = animations.get(i);
283 
284                 temp.clear();
285                 a.applyTransformation(0.0f, temp);
286                 previousTransformation.compose(temp);
287             }
288         }
289     }
290 
291     /**
292      * The transformation of an animation set is the concatenation of all of its
293      * component animations.
294      *
295      * @see android.view.animation.Animation#getTransformation
296      */
297     @Override
getTransformation(long currentTime, Transformation t)298     public boolean getTransformation(long currentTime, Transformation t) {
299         final int count = mAnimations.size();
300         final ArrayList<Animation> animations = mAnimations;
301         final Transformation temp = mTempTransformation;
302 
303         boolean more = false;
304         boolean started = false;
305         boolean ended = true;
306 
307         t.clear();
308 
309         for (int i = count - 1; i >= 0; --i) {
310             final Animation a = animations.get(i);
311 
312             temp.clear();
313             more = a.getTransformation(currentTime, temp) || more;
314             t.compose(temp);
315 
316             started = started || a.hasStarted();
317             ended = a.hasEnded() && ended;
318         }
319 
320         if (started && !mStarted) {
321             if (mListener != null) {
322                 mListener.onAnimationStart(this);
323             }
324             mStarted = true;
325         }
326 
327         if (ended != mEnded) {
328             if (mListener != null) {
329                 mListener.onAnimationEnd(this);
330             }
331             mEnded = ended;
332         }
333 
334         return more;
335     }
336 
337     /**
338      * @see android.view.animation.Animation#scaleCurrentDuration(float)
339      */
340     @Override
scaleCurrentDuration(float scale)341     public void scaleCurrentDuration(float scale) {
342         final ArrayList<Animation> animations = mAnimations;
343         int count = animations.size();
344         for (int i = 0; i < count; i++) {
345             animations.get(i).scaleCurrentDuration(scale);
346         }
347     }
348 
349     /**
350      * @see android.view.animation.Animation#initialize(int, int, int, int)
351      */
352     @Override
initialize(int width, int height, int parentWidth, int parentHeight)353     public void initialize(int width, int height, int parentWidth, int parentHeight) {
354         super.initialize(width, height, parentWidth, parentHeight);
355 
356         boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK;
357         boolean fillAfterSet = (mFlags & PROPERTY_FILL_AFTER_MASK) == PROPERTY_FILL_AFTER_MASK;
358         boolean fillBeforeSet = (mFlags & PROPERTY_FILL_BEFORE_MASK) == PROPERTY_FILL_BEFORE_MASK;
359         boolean repeatModeSet = (mFlags & PROPERTY_REPEAT_MODE_MASK) == PROPERTY_REPEAT_MODE_MASK;
360         boolean shareInterpolator = (mFlags & PROPERTY_SHARE_INTERPOLATOR_MASK)
361                 == PROPERTY_SHARE_INTERPOLATOR_MASK;
362         boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK)
363                 == PROPERTY_START_OFFSET_MASK;
364 
365         if (shareInterpolator) {
366             ensureInterpolator();
367         }
368 
369         final ArrayList<Animation> children = mAnimations;
370         final int count = children.size();
371 
372         final long duration = mDuration;
373         final boolean fillAfter = mFillAfter;
374         final boolean fillBefore = mFillBefore;
375         final int repeatMode = mRepeatMode;
376         final Interpolator interpolator = mInterpolator;
377         final long startOffset = mStartOffset;
378 
379 
380         long[] storedOffsets = mStoredOffsets;
381         if (startOffsetSet) {
382             if (storedOffsets == null || storedOffsets.length != count) {
383                 storedOffsets = mStoredOffsets = new long[count];
384             }
385         } else if (storedOffsets != null) {
386             storedOffsets = mStoredOffsets = null;
387         }
388 
389         for (int i = 0; i < count; i++) {
390             Animation a = children.get(i);
391             if (durationSet) {
392                 a.setDuration(duration);
393             }
394             if (fillAfterSet) {
395                 a.setFillAfter(fillAfter);
396             }
397             if (fillBeforeSet) {
398                 a.setFillBefore(fillBefore);
399             }
400             if (repeatModeSet) {
401                 a.setRepeatMode(repeatMode);
402             }
403             if (shareInterpolator) {
404                 a.setInterpolator(interpolator);
405             }
406             if (startOffsetSet) {
407                 long offset = a.getStartOffset();
408                 a.setStartOffset(offset + startOffset);
409                 storedOffsets[i] = offset;
410             }
411             a.initialize(width, height, parentWidth, parentHeight);
412         }
413     }
414 
415     @Override
reset()416     public void reset() {
417         super.reset();
418         restoreChildrenStartOffset();
419     }
420 
421     /**
422      * @hide
423      */
restoreChildrenStartOffset()424     void restoreChildrenStartOffset() {
425         final long[] offsets = mStoredOffsets;
426         if (offsets == null) return;
427 
428         final ArrayList<Animation> children = mAnimations;
429         final int count = children.size();
430 
431         for (int i = 0; i < count; i++) {
432             children.get(i).setStartOffset(offsets[i]);
433         }
434     }
435 
436     /**
437      * @return All the child animations in this AnimationSet. Note that
438      * this may include other AnimationSets, which are not expanded.
439      */
getAnimations()440     public List<Animation> getAnimations() {
441         return mAnimations;
442     }
443 
444     @Override
willChangeTransformationMatrix()445     public boolean willChangeTransformationMatrix() {
446         return (mFlags & PROPERTY_MORPH_MATRIX_MASK) == PROPERTY_MORPH_MATRIX_MASK;
447     }
448 
449     @Override
willChangeBounds()450     public boolean willChangeBounds() {
451         return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK;
452     }
453 }
454