diff options
Diffstat (limited to 'core/java/android/view/animation/AnimationSet.java')
-rw-r--r-- | core/java/android/view/animation/AnimationSet.java | 472 |
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; + } +} |