summaryrefslogtreecommitdiffstats
path: root/core/java/android/view/animation/AnimationSet.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/view/animation/AnimationSet.java')
-rw-r--r--core/java/android/view/animation/AnimationSet.java472
1 files changed, 472 insertions, 0 deletions
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
new file mode 100644
index 0000000..7b56f00
--- /dev/null
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.graphics.RectF;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a group of Animations that should be played together.
+ * The transformation of each individual animation are composed
+ * together into a single transform.
+ * If AnimationSet sets any properties that its children also set
+ * (for example, duration or fillBefore), the values of AnimationSet
+ * override the child values.
+ */
+public class AnimationSet extends Animation {
+ private static final int PROPERTY_FILL_AFTER_MASK = 0x1;
+ private static final int PROPERTY_FILL_BEFORE_MASK = 0x2;
+ private static final int PROPERTY_REPEAT_MODE_MASK = 0x4;
+ private static final int PROPERTY_START_OFFSET_MASK = 0x8;
+ private static final int PROPERTY_SHARE_INTERPOLATOR_MASK = 0x10;
+ private static final int PROPERTY_DURATION_MASK = 0x20;
+ private static final int PROPERTY_MORPH_MATRIX_MASK = 0x40;
+ private static final int PROPERTY_CHANGE_BOUNDS_MASK = 0x80;
+
+ private int mFlags = 0;
+
+ private ArrayList<Animation> mAnimations = new ArrayList<Animation>();
+
+ private Transformation mTempTransformation = new Transformation();
+
+ private long mLastEnd;
+
+ private long[] mStoredOffsets;
+
+ /**
+ * Constructor used whan an AnimationSet is loaded from a resource.
+ *
+ * @param context Application context to use
+ * @param attrs Attribute set from which to read values
+ */
+ public AnimationSet(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a =
+ context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AnimationSet);
+
+ setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK,
+ a.getBoolean(com.android.internal.R.styleable.AnimationSet_shareInterpolator, true));
+ init();
+
+ a.recycle();
+ }
+
+
+ /**
+ * Constructor to use when building an AnimationSet from code
+ *
+ * @param shareInterpolator Pass true if all of the animations in this set
+ * should use the interpolator assocciated with this AnimationSet.
+ * Pass false if each animation should use its own interpolator.
+ */
+ public AnimationSet(boolean shareInterpolator) {
+ setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK, shareInterpolator);
+ init();
+ }
+
+ @Override
+ protected AnimationSet clone() throws CloneNotSupportedException {
+ final AnimationSet animation = (AnimationSet) super.clone();
+ animation.mTempTransformation = new Transformation();
+ animation.mAnimations = new ArrayList<Animation>();
+
+ final int count = mAnimations.size();
+ final ArrayList<Animation> animations = mAnimations;
+
+ for (int i = 0; i < count; i++) {
+ animation.mAnimations.add(animations.get(i).clone());
+ }
+
+ return animation;
+ }
+
+ private void setFlag(int mask, boolean value) {
+ if (value) {
+ mFlags |= mask;
+ } else {
+ mFlags &= ~mask;
+ }
+ }
+
+ private void init() {
+ mStartTime = 0;
+ mDuration = 0;
+ }
+
+ @Override
+ public void setFillAfter(boolean fillAfter) {
+ mFlags |= PROPERTY_FILL_AFTER_MASK;
+ super.setFillAfter(fillAfter);
+ }
+
+ @Override
+ public void setFillBefore(boolean fillBefore) {
+ mFlags |= PROPERTY_FILL_BEFORE_MASK;
+ super.setFillBefore(fillBefore);
+ }
+
+ @Override
+ public void setRepeatMode(int repeatMode) {
+ mFlags |= PROPERTY_REPEAT_MODE_MASK;
+ super.setRepeatMode(repeatMode);
+ }
+
+ @Override
+ public void setStartOffset(long startOffset) {
+ mFlags |= PROPERTY_START_OFFSET_MASK;
+ super.setStartOffset(startOffset);
+ }
+
+ /**
+ * <p>Sets the duration of every child animation.</p>
+ *
+ * @param durationMillis the duration of the animation, in milliseconds, for
+ * every child in this set
+ */
+ @Override
+ public void setDuration(long durationMillis) {
+ mFlags |= PROPERTY_DURATION_MASK;
+ super.setDuration(durationMillis);
+ }
+
+ /**
+ * Add a child animation to this animation set.
+ * The transforms of the child animations are applied in the order
+ * that they were added
+ * @param a Animation to add.
+ */
+ public void addAnimation(Animation a) {
+ mAnimations.add(a);
+
+ boolean noMatrix = (mFlags & PROPERTY_MORPH_MATRIX_MASK) == 0;
+ if (noMatrix && a.willChangeTransformationMatrix()) {
+ mFlags |= PROPERTY_MORPH_MATRIX_MASK;
+ }
+
+ boolean changeBounds = (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == 0;
+ if (changeBounds && a.willChangeTransformationMatrix()) {
+ mFlags |= PROPERTY_CHANGE_BOUNDS_MASK;
+ }
+
+ if (mAnimations.size() == 1) {
+ mDuration = a.getStartOffset() + a.getDuration();
+ mLastEnd = mStartOffset + mDuration;
+ } else {
+ mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration());
+ mDuration = mLastEnd - mStartOffset;
+ }
+ }
+
+ /**
+ * Sets the start time of this animation and all child animations
+ *
+ * @see android.view.animation.Animation#setStartTime(long)
+ */
+ @Override
+ public void setStartTime(long startTimeMillis) {
+ super.setStartTime(startTimeMillis);
+
+ final int count = mAnimations.size();
+ final ArrayList<Animation> animations = mAnimations;
+
+ for (int i = 0; i < count; i++) {
+ Animation a = animations.get(i);
+ a.setStartTime(startTimeMillis);
+ }
+ }
+
+ @Override
+ public long getStartTime() {
+ long startTime = Long.MAX_VALUE;
+
+ final int count = mAnimations.size();
+ final ArrayList<Animation> animations = mAnimations;
+
+ for (int i = 0; i < count; i++) {
+ Animation a = animations.get(i);
+ startTime = Math.min(startTime, a.getStartTime());
+ }
+
+ return startTime;
+ }
+
+ @Override
+ public void restrictDuration(long durationMillis) {
+ super.restrictDuration(durationMillis);
+
+ final ArrayList<Animation> animations = mAnimations;
+ int count = animations.size();
+
+ for (int i = 0; i < count; i++) {
+ animations.get(i).restrictDuration(durationMillis);
+ }
+ }
+
+ /**
+ * The duration of an AnimationSet is defined to be the
+ * duration of the longest child animation.
+ *
+ * @see android.view.animation.Animation#getDuration()
+ */
+ @Override
+ public long getDuration() {
+ final ArrayList<Animation> animations = mAnimations;
+ final int count = animations.size();
+ long duration = 0;
+
+ boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK;
+ if (durationSet) {
+ duration = mDuration;
+ } else {
+ for (int i = 0; i < count; i++) {
+ duration = Math.max(duration, animations.get(i).getDuration());
+ }
+ }
+
+ return duration;
+ }
+
+ /**
+ * The duration hint of an animation set is the maximum of the duration
+ * hints of all of its component animations.
+ *
+ * @see android.view.animation.Animation#computeDurationHint
+ */
+ public long computeDurationHint() {
+ long duration = 0;
+ final int count = mAnimations.size();
+ final ArrayList<Animation> animations = mAnimations;
+ for (int i = count - 1; i >= 0; --i) {
+ final long d = animations.get(i).computeDurationHint();
+ if (d > duration) duration = d;
+ }
+ return duration;
+ }
+
+ /**
+ * @hide
+ */
+ public void getInvalidateRegion(int left, int top, int right, int bottom,
+ RectF invalidate, Transformation transformation) {
+
+ final RectF previousRegion = mPreviousRegion;
+
+ invalidate.set(left, top, right, bottom);
+ transformation.getMatrix().mapRect(invalidate);
+ invalidate.union(previousRegion);
+
+ previousRegion.set(left, top, right, bottom);
+ transformation.getMatrix().mapRect(previousRegion);
+
+ final Transformation tempTransformation = mTransformation;
+ final Transformation previousTransformation = mPreviousTransformation;
+
+ tempTransformation.set(transformation);
+ transformation.set(previousTransformation);
+ previousTransformation.set(tempTransformation);
+ }
+
+ /**
+ * @hide
+ */
+ public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
+ final RectF region = mPreviousRegion;
+ region.set(left, top, right, bottom);
+
+ if (mFillBefore) {
+ final int count = mAnimations.size();
+ final ArrayList<Animation> animations = mAnimations;
+ final Transformation temp = mTempTransformation;
+
+ final Transformation previousTransformation = mPreviousTransformation;
+
+ for (int i = count - 1; i >= 0; --i) {
+ final Animation a = animations.get(i);
+
+ temp.clear();
+ a.applyTransformation(0.0f, temp);
+ previousTransformation.compose(temp);
+ }
+ }
+ }
+
+ /**
+ * The transformation of an animation set is the concatenation of all of its
+ * component animations.
+ *
+ * @see android.view.animation.Animation#getTransformation
+ */
+ @Override
+ public boolean getTransformation(long currentTime, Transformation t) {
+ final int count = mAnimations.size();
+ final ArrayList<Animation> animations = mAnimations;
+ final Transformation temp = mTempTransformation;
+
+ boolean more = false;
+ boolean started = false;
+ boolean ended = true;
+
+ t.clear();
+
+ for (int i = count - 1; i >= 0; --i) {
+ final Animation a = animations.get(i);
+
+ temp.clear();
+ more = a.getTransformation(currentTime, temp) || more;
+ t.compose(temp);
+
+ started = started || a.hasStarted();
+ ended = a.hasEnded() && ended;
+ }
+
+ if (started && !mStarted) {
+ if (mListener != null) {
+ mListener.onAnimationStart(this);
+ }
+ mStarted = true;
+ }
+
+ if (ended != mEnded) {
+ if (mListener != null) {
+ mListener.onAnimationEnd(this);
+ }
+ mEnded = ended;
+ }
+
+ return more;
+ }
+
+ /**
+ * @see android.view.animation.Animation#scaleCurrentDuration(float)
+ */
+ @Override
+ public void scaleCurrentDuration(float scale) {
+ final ArrayList<Animation> animations = mAnimations;
+ int count = animations.size();
+ for (int i = 0; i < count; i++) {
+ animations.get(i).scaleCurrentDuration(scale);
+ }
+ }
+
+ /**
+ * @see android.view.animation.Animation#initialize(int, int, int, int)
+ */
+ @Override
+ public void initialize(int width, int height, int parentWidth, int parentHeight) {
+ super.initialize(width, height, parentWidth, parentHeight);
+
+ boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK;
+ boolean fillAfterSet = (mFlags & PROPERTY_FILL_AFTER_MASK) == PROPERTY_FILL_AFTER_MASK;
+ boolean fillBeforeSet = (mFlags & PROPERTY_FILL_BEFORE_MASK) == PROPERTY_FILL_BEFORE_MASK;
+ boolean repeatModeSet = (mFlags & PROPERTY_REPEAT_MODE_MASK) == PROPERTY_REPEAT_MODE_MASK;
+ boolean shareInterpolator = (mFlags & PROPERTY_SHARE_INTERPOLATOR_MASK)
+ == PROPERTY_SHARE_INTERPOLATOR_MASK;
+ boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK)
+ == PROPERTY_START_OFFSET_MASK;
+
+ if (shareInterpolator) {
+ ensureInterpolator();
+ }
+
+ final ArrayList<Animation> children = mAnimations;
+ final int count = children.size();
+
+ final long duration = mDuration;
+ final boolean fillAfter = mFillAfter;
+ final boolean fillBefore = mFillBefore;
+ final int repeatMode = mRepeatMode;
+ final Interpolator interpolator = mInterpolator;
+ final long startOffset = mStartOffset;
+
+
+ long[] storedOffsets = mStoredOffsets;
+ if (storedOffsets == null || storedOffsets.length != count) {
+ storedOffsets = mStoredOffsets = new long[count];
+ }
+
+ for (int i = 0; i < count; i++) {
+ Animation a = children.get(i);
+ if (durationSet) {
+ a.setDuration(duration);
+ }
+ if (fillAfterSet) {
+ a.setFillAfter(fillAfter);
+ }
+ if (fillBeforeSet) {
+ a.setFillBefore(fillBefore);
+ }
+ if (repeatModeSet) {
+ a.setRepeatMode(repeatMode);
+ }
+ if (shareInterpolator) {
+ a.setInterpolator(interpolator);
+ }
+ if (startOffsetSet) {
+ long offset = a.getStartOffset();
+ a.setStartOffset(offset + startOffset);
+ storedOffsets[i] = offset;
+ }
+ a.initialize(width, height, parentWidth, parentHeight);
+ }
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ restoreChildrenStartOffset();
+ }
+
+ /**
+ * @hide
+ */
+ void restoreChildrenStartOffset() {
+ final long[] offsets = mStoredOffsets;
+ if (offsets == null) return;
+
+ final ArrayList<Animation> children = mAnimations;
+ final int count = children.size();
+
+
+ for (int i = 0; i < count; i++) {
+ children.get(i).setStartOffset(offsets[i]);
+ }
+ }
+
+ /**
+ * @return All the child animations in this AnimationSet. Note that
+ * this may include other AnimationSets, which are not expanded.
+ */
+ public List<Animation> getAnimations() {
+ return mAnimations;
+ }
+
+ @Override
+ public boolean willChangeTransformationMatrix() {
+ return (mFlags & PROPERTY_MORPH_MATRIX_MASK) == PROPERTY_MORPH_MATRIX_MASK;
+ }
+
+ @Override
+ public boolean willChangeBounds() {
+ return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK;
+ }
+}