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