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