Merge "Rename several animation classes"

This commit is contained in:
Chet Haase
2010-09-08 10:25:19 -07:00
committed by Android (Google) Code Review
27 changed files with 1963 additions and 1980 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,248 +0,0 @@
/*
* Copyright (C) 2010 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.animation;
import android.view.animation.Interpolator;
import java.util.ArrayList;
/**
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatableListeners</code> added to them.
*/
public abstract class Animatable implements Cloneable {
/**
* The set of listeners to be sent events through the life of an animation.
*/
ArrayList<AnimatableListener> mListeners = null;
/**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. Note that the animation does not start synchronously with
* this call, because all animation events are posted to a central timing loop so that animation
* times are all synchronized on a single timing pulse on the UI thread. So the animation will
* start the next time that event handler processes events.
*/
public void start() {
}
/**
* Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
* stop in its tracks, sending an {@link AnimatableListener#onAnimationCancel(Animatable)} to
* its listeners, followed by an {@link AnimatableListener#onAnimationEnd(Animatable)} message.
*/
public void cancel() {
}
/**
* Ends the animation. This causes the animation to assign the end value of the property being
* animated, then calling the {@link AnimatableListener#onAnimationEnd(Animatable)} method on
* its listeners.
*/
public void end() {
}
/**
* The amount of time, in milliseconds, to delay starting the animation after
* {@link #start()} is called.
*
* @return the number of milliseconds to delay running the animation
*/
public abstract long getStartDelay();
/**
* The amount of time, in milliseconds, to delay starting the animation after
* {@link #start()} is called.
* @param startDelay The amount of the delay, in milliseconds
*/
public abstract void setStartDelay(long startDelay);
/**
* Sets the length of the animation.
*
* @param duration The length of the animation, in milliseconds.
*/
public abstract void setDuration(long duration);
/**
* Gets the length of the animation.
*
* @return The length of the animation, in milliseconds.
*/
public abstract long getDuration();
/**
* The time interpolator used in calculating the elapsed fraction of this animation. The
* interpolator determines whether the animation runs with linear or non-linear motion,
* such as acceleration and deceleration. The default value is
* {@link android.view.animation.AccelerateDecelerateInterpolator}
*
* @param value the interpolator to be used by this animation
*/
public abstract void setInterpolator(Interpolator value);
/**
* Returns whether this Animatable is currently running (having been started and not yet ended).
* @return Whether the Animatable is running.
*/
public abstract boolean isRunning();
/**
* Adds a listener to the set of listeners that are sent events through the life of an
* animation, such as start, repeat, and end.
*
* @param listener the listener to be added to the current set of listeners for this animation.
*/
public void addListener(AnimatableListener listener) {
if (mListeners == null) {
mListeners = new ArrayList<AnimatableListener>();
}
mListeners.add(listener);
}
/**
* Removes a listener from the set listening to this animation.
*
* @param listener the listener to be removed from the current set of listeners for this
* animation.
*/
public void removeListener(AnimatableListener listener) {
if (mListeners == null) {
return;
}
mListeners.remove(listener);
if (mListeners.size() == 0) {
mListeners = null;
}
}
/**
* Gets the set of {@link AnimatableListener} objects that are currently
* listening for events on this <code>Animatable</code> object.
*
* @return ArrayList<AnimatableListener> The set of listeners.
*/
public ArrayList<AnimatableListener> getListeners() {
return mListeners;
}
/**
* Removes all listeners from this object. This is equivalent to calling
* <code>getListeners()</code> followed by calling <code>clear()</code> on the
* returned list of listeners.
*/
public void removeAllListeners() {
if (mListeners != null) {
mListeners.clear();
mListeners = null;
}
}
@Override
public Animatable clone() {
try {
final Animatable anim = (Animatable) super.clone();
if (mListeners != null) {
ArrayList<AnimatableListener> oldListeners = mListeners;
anim.mListeners = new ArrayList<AnimatableListener>();
int numListeners = oldListeners.size();
for (int i = 0; i < numListeners; ++i) {
anim.mListeners.add(oldListeners.get(i));
}
}
return anim;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
/**
* This method tells the object to use appropriate information to extract
* starting values for the animation. For example, a Sequencer object will pass
* this call to its child objects to tell them to set up the values. A
* PropertyAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
* An Animator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupStartValues() {
}
/**
* This method tells the object to use appropriate information to extract
* ending values for the animation. For example, a Sequencer object will pass
* this call to its child objects to tell them to set up the values. A
* PropertyAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
* An Animator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupEndValues() {
}
/**
* Sets the target object whose property will be animated by this animation. Not all subclasses
* operate on target objects (for example, {@link android.animation.Animator}, but this method
* is on the superclass for the convenience of dealing generically with those subclasses
* that do handle targets.
*
* @param target The object being animated
*/
public void setTarget(Object target) {
}
/**
* <p>An animation listener receives notifications from an animation.
* Notifications indicate animation related events, such as the end or the
* repetition of the animation.</p>
*/
public static interface AnimatableListener {
/**
* <p>Notifies the start of the animation.</p>
*
* @param animation The started animation.
*/
void onAnimationStart(Animatable animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd(Animatable animation);
/**
* <p>Notifies the cancellation of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which was canceled.
*/
void onAnimationCancel(Animatable animation);
/**
* <p>Notifies the repetition of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationRepeat(Animatable animation);
}
}

1023
core/java/android/animation/Animator.java Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@ import java.io.IOException;
import java.util.ArrayList;
/**
* This class is used to instantiate menu XML files into Animatable objects.
* This class is used to instantiate menu XML files into Animator objects.
* <p>
* For performance reasons, menu inflation relies heavily on pre-processing of
* XML files that is done at build time. Therefore, it is not currently possible
@@ -38,10 +38,10 @@ import java.util.ArrayList;
* it only works with an XmlPullParser returned from a compiled resource (R.
* <em>something</em> file.)
*/
public class AnimatableInflater {
public class AnimatorInflater {
/**
* These flags are used when parsing Sequencer objects
* These flags are used when parsing AnimatorSet objects
*/
private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;
@@ -56,20 +56,20 @@ public class AnimatableInflater {
private static final int VALUE_TYPE_CUSTOM = 4;
/**
* Loads an {@link Animatable} object from a resource
* Loads an {@link Animator} object from a resource
*
* @param context Application context used to access resources
* @param id The resource id of the animation to load
* @return The animatable object reference by the specified id
* @return The animator object reference by the specified id
* @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
*/
public static Animatable loadAnimatable(Context context, int id)
public static Animator loadAnimator(Context context, int id)
throws NotFoundException {
XmlResourceParser parser = null;
try {
parser = context.getResources().getAnimation(id);
return createAnimatableFromXml(context, parser);
return createAnimatorFromXml(context, parser);
} catch (XmlPullParserException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException("Can't load animation resource ID #0x" +
@@ -87,18 +87,18 @@ public class AnimatableInflater {
}
}
private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser)
private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
throws XmlPullParserException, IOException {
return createAnimatableFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
}
private static Animatable createAnimatableFromXml(Context c, XmlPullParser parser,
AttributeSet attrs, Sequencer parent, int sequenceOrdering)
private static Animator createAnimatorFromXml(Context c, XmlPullParser parser,
AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
throws XmlPullParserException, IOException {
Animatable anim = null;
ArrayList<Animatable> childAnims = null;
Animator anim = null;
ArrayList<Animator> childAnims = null;
// Make sure we are on a start tag.
int type;
@@ -113,17 +113,17 @@ public class AnimatableInflater {
String name = parser.getName();
if (name.equals("property")) {
anim = loadPropertyAnimator(c, attrs);
if (name.equals("objectAnimator")) {
anim = loadObjectAnimator(c, attrs);
} else if (name.equals("animator")) {
anim = loadAnimator(c, attrs, null);
} else if (name.equals("sequencer")) {
anim = new Sequencer();
} else if (name.equals("set")) {
anim = new AnimatorSet();
TypedArray a = c.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.Sequencer);
int ordering = a.getInt(com.android.internal.R.styleable.Sequencer_ordering,
com.android.internal.R.styleable.AnimatorSet);
int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering,
TOGETHER);
createAnimatableFromXml(c, parser, attrs, (Sequencer) anim, ordering);
createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim, ordering);
a.recycle();
} else {
throw new RuntimeException("Unknown animator name: " + parser.getName());
@@ -131,15 +131,15 @@ public class AnimatableInflater {
if (parent != null) {
if (childAnims == null) {
childAnims = new ArrayList<Animatable>();
childAnims = new ArrayList<Animator>();
}
childAnims.add(anim);
}
}
if (parent != null && childAnims != null) {
Animatable[] animsArray = new Animatable[childAnims.size()];
Animator[] animsArray = new Animator[childAnims.size()];
int index = 0;
for (Animatable a : childAnims) {
for (Animator a : childAnims) {
animsArray[index++] = a;
}
if (sequenceOrdering == TOGETHER) {
@@ -153,10 +153,10 @@ public class AnimatableInflater {
}
private static PropertyAnimator loadPropertyAnimator(Context context, AttributeSet attrs)
private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs)
throws NotFoundException {
PropertyAnimator anim = new PropertyAnimator();
ObjectAnimator anim = new ObjectAnimator();
loadAnimator(context, attrs, anim);
@@ -179,7 +179,7 @@ public class AnimatableInflater {
* @param context the application environment
* @param attrs the set of attributes holding the animation parameters
*/
private static Animator loadAnimator(Context context, AttributeSet attrs, Animator anim)
private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim)
throws NotFoundException {
TypedArray a =
@@ -236,7 +236,7 @@ public class AnimatableInflater {
}
if (anim == null) {
anim = new Animator(duration, valueFrom, valueTo);
anim = new ValueAnimator(duration, valueFrom, valueTo);
} else {
anim.setDuration(duration);
anim.setValues(valueFrom, valueTo);
@@ -251,7 +251,7 @@ public class AnimatableInflater {
if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) {
anim.setRepeatMode(
a.getInt(com.android.internal.R.styleable.Animator_repeatMode,
Animator.RESTART));
ValueAnimator.RESTART));
}
if (evaluator != null) {
anim.setEvaluator(evaluator);

View File

@@ -16,41 +16,39 @@
package android.animation;
import android.animation.Animatable.AnimatableListener;
/**
* This adapter class provides empty implementations of the methods from {@link AnimatableListener}.
* This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}.
* Any custom listener that cares only about a subset of the methods of this listener can
* simply subclass this adapter class instead of implementing the interface directly.
*/
public abstract class AnimatableListenerAdapter implements AnimatableListener {
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener {
/**
* {@inheritdoc}
*/
@Override
public void onAnimationCancel(Animatable animation) {
public void onAnimationCancel(Animator animation) {
}
/**
* {@inheritdoc}
*/
@Override
public void onAnimationEnd(Animatable animation) {
public void onAnimationEnd(Animator animation) {
}
/**
* {@inheritdoc}
*/
@Override
public void onAnimationRepeat(Animatable animation) {
public void onAnimationRepeat(Animator animation) {
}
/**
* {@inheritdoc}
*/
@Override
public void onAnimationStart(Animatable animation) {
public void onAnimationStart(Animator animation) {
}
}

View File

@@ -16,35 +16,31 @@
package android.animation;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import java.util.ArrayList;
import java.util.HashMap;
/**
* This class plays a set of {@link Animatable} objects in the specified order. Animations
* This class plays a set of {@link Animator} objects in the specified order. Animations
* can be set up to play together, in sequence, or after a specified delay.
*
* <p>There are two different approaches to adding animations to a <code>Sequencer</code>:
* either the {@link Sequencer#playTogether(Animatable[]) playTogether()} or
* {@link Sequencer#playSequentially(Animatable[]) playSequentially()} methods can be called to add
* a set of animations all at once, or the {@link Sequencer#play(Animatable)} can be
* used in conjunction with methods in the {@link android.animation.Sequencer.Builder Builder}
* <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
* either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
* {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
* a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
* used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
* class to add animations
* one by one.</p>
*
* <p>It is possible to set up a <code>Sequencer</code> with circular dependencies between
* <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
* its animations. For example, an animation a1 could be set up to start before animation a2, a2
* before a3, and a3 before a1. The results of this configuration are undefined, but will typically
* result in none of the affected animations being played. Because of this (and because
* circular dependencies do not make logical sense anyway), circular dependencies
* should be avoided, and the dependency flow of animations should only be in one direction.
*/
public final class Sequencer extends Animatable {
public final class AnimatorSet extends Animator {
/**
* Internal variables
@@ -55,21 +51,21 @@ public final class Sequencer extends Animatable {
/**
* Tracks animations currently being played, so that we know what to
* cancel or end when cancel() or end() is called on this Sequencer
* cancel or end when cancel() or end() is called on this AnimatorSet
*/
private ArrayList<Animatable> mPlayingSet = new ArrayList<Animatable>();
private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
/**
* Contains all nodes, mapped to their respective Animatables. When new
* dependency information is added for an Animatable, we want to add it
* to a single node representing that Animatable, not create a new Node
* Contains all nodes, mapped to their respective Animators. When new
* dependency information is added for an Animator, we want to add it
* to a single node representing that Animator, not create a new Node
* if one already exists.
*/
private HashMap<Animatable, Node> mNodeMap = new HashMap<Animatable, Node>();
private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>();
/**
* Set of all nodes created for this Sequencer. This list is used upon
* starting the sequencer, and the nodes are placed in sorted order into the
* Set of all nodes created for this AnimatorSet. This list is used upon
* starting the set, and the nodes are placed in sorted order into the
* sortedNodes collection.
*/
private ArrayList<Node> mNodes = new ArrayList<Node>();
@@ -88,12 +84,12 @@ public final class Sequencer extends Animatable {
*/
private boolean mNeedsSort = true;
private SequencerAnimatableListener mSequenceListener = null;
private AnimatorSetListener mSetListener = null;
/**
* Flag indicating that the Sequencer has been canceled (by calling cancel() or end()).
* Flag indicating that the AnimatorSet has been canceled (by calling cancel() or end()).
* This flag is used to avoid starting other animations when currently-playing
* child animations of this Sequencer end.
* child animations of this AnimatorSet end.
*/
boolean mCanceled = false;
@@ -102,55 +98,55 @@ public final class Sequencer extends Animatable {
// How long the child animations should last in ms. The default value is negative, which
// simply means that there is no duration set on the Sequencer. When a real duration is
// simply means that there is no duration set on the AnimatorSet. When a real duration is
// set, it is passed along to the child animations.
private long mDuration = -1;
/**
* Sets up this Sequencer to play all of the supplied animations at the same time.
* Sets up this AnimatorSet to play all of the supplied animations at the same time.
*
* @param sequenceItems The animations that will be started simultaneously.
* @param items The animations that will be started simultaneously.
*/
public void playTogether(Animatable... sequenceItems) {
if (sequenceItems != null) {
public void playTogether(Animator... items) {
if (items != null) {
mNeedsSort = true;
Builder builder = play(sequenceItems[0]);
for (int i = 1; i < sequenceItems.length; ++i) {
builder.with(sequenceItems[i]);
Builder builder = play(items[0]);
for (int i = 1; i < items.length; ++i) {
builder.with(items[i]);
}
}
}
/**
* Sets up this Sequencer to play each of the supplied animations when the
* Sets up this AnimatorSet to play each of the supplied animations when the
* previous animation ends.
*
* @param sequenceItems The aniamtions that will be started one after another.
* @param items The aniamtions that will be started one after another.
*/
public void playSequentially(Animatable... sequenceItems) {
if (sequenceItems != null) {
public void playSequentially(Animator... items) {
if (items != null) {
mNeedsSort = true;
if (sequenceItems.length == 1) {
play(sequenceItems[0]);
if (items.length == 1) {
play(items[0]);
} else {
for (int i = 0; i < sequenceItems.length - 1; ++i) {
play(sequenceItems[i]).before(sequenceItems[i+1]);
for (int i = 0; i < items.length - 1; ++i) {
play(items[i]).before(items[i+1]);
}
}
}
}
/**
* Returns the current list of child Animatable objects controlled by this
* Sequencer. This is a copy of the internal list; modifications to the returned list
* will not affect the Sequencer, although changes to the underlying Animatable objects
* will affect those objects being managed by the Sequencer.
* Returns the current list of child Animator objects controlled by this
* AnimatorSet. This is a copy of the internal list; modifications to the returned list
* will not affect the AnimatorSet, although changes to the underlying Animator objects
* will affect those objects being managed by the AnimatorSet.
*
* @return ArrayList<Animatable> The list of child animations of this Sequencer.
* @return ArrayList<Animator> The list of child animations of this AnimatorSet.
*/
public ArrayList<Animatable> getChildAnimations() {
ArrayList<Animatable> childList = new ArrayList<Animatable>();
public ArrayList<Animator> getChildAnimations() {
ArrayList<Animator> childList = new ArrayList<Animator>();
for (Node node : mNodes) {
childList.add(node.animation);
}
@@ -159,28 +155,28 @@ public final class Sequencer extends Animatable {
/**
* Sets the target object for all current {@link #getChildAnimations() child animations}
* of this Sequencer that take targets ({@link android.animation.PropertyAnimator} and
* Sequencer).
* of this AnimatorSet that take targets ({@link ObjectAnimator} and
* AnimatorSet).
*
* @param target The object being animated
*/
@Override
public void setTarget(Object target) {
for (Node node : mNodes) {
Animatable animation = node.animation;
if (animation instanceof Sequencer) {
((Sequencer)animation).setTarget(target);
} else if (animation instanceof PropertyAnimator) {
((PropertyAnimator)animation).setTarget(target);
Animator animation = node.animation;
if (animation instanceof AnimatorSet) {
((AnimatorSet)animation).setTarget(target);
} else if (animation instanceof ObjectAnimator) {
((ObjectAnimator)animation).setTarget(target);
}
}
}
/**
* Sets the Interpolator for all current {@link #getChildAnimations() child animations}
* of this Sequencer.
* of this AnimatorSet.
*
* @param interpolator the interpolator to be used by each child animation of this Sequencer
* @param interpolator the interpolator to be used by each child animation of this AnimatorSet
*/
@Override
public void setInterpolator(Interpolator interpolator) {
@@ -194,11 +190,11 @@ public final class Sequencer extends Animatable {
* set up playing constraints. This initial <code>play()</code> method
* tells the <code>Builder</code> the animation that is the dependency for
* the succeeding commands to the <code>Builder</code>. For example,
* calling <code>play(a1).with(a2)</code> sets up the Sequence to play
* calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
* <code>a1</code> and <code>a2</code> at the same time,
* <code>play(a1).before(a2)</code> sets up the Sequence to play
* <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
* <code>a1</code> first, followed by <code>a2</code>, and
* <code>play(a1).after(a2)</code> sets up the Sequence to play
* <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
* <code>a2</code> first, followed by <code>a1</code>.
*
* <p>Note that <code>play()</code> is the only way to tell the
@@ -213,11 +209,11 @@ public final class Sequencer extends Animatable {
* @param anim The animation that is the dependency used in later calls to the
* methods in the returned <code>Builder</code> object. A null parameter will result
* in a null <code>Builder</code> return value.
* @return Builder The object that constructs the sequence based on the dependencies
* @return Builder The object that constructs the AnimatorSet based on the dependencies
* outlined in the calls to <code>play</code> and the other methods in the
* <code>Builder</code object.
*/
public Builder play(Animatable anim) {
public Builder play(Animator anim) {
if (anim != null) {
mNeedsSort = true;
return new Builder(anim);
@@ -228,7 +224,7 @@ public final class Sequencer extends Animatable {
/**
* {@inheritDoc}
*
* <p>Note that canceling a <code>Sequencer</code> also cancels all of the animations that it is
* <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it is
* responsible for.</p>
*/
@SuppressWarnings("unchecked")
@@ -236,9 +232,9 @@ public final class Sequencer extends Animatable {
public void cancel() {
mCanceled = true;
if (mListeners != null) {
ArrayList<AnimatableListener> tmpListeners =
(ArrayList<AnimatableListener>) mListeners.clone();
for (AnimatableListener listener : tmpListeners) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
for (AnimatorListener listener : tmpListeners) {
listener.onAnimationCancel(this);
}
}
@@ -252,7 +248,7 @@ public final class Sequencer extends Animatable {
/**
* {@inheritDoc}
*
* <p>Note that ending a <code>Sequencer</code> also ends all of the animations that it is
* <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
* responsible for.</p>
*/
@Override
@@ -262,10 +258,10 @@ public final class Sequencer extends Animatable {
// hasn't been started yet - sort the nodes now, then end them
sortNodes();
for (Node node : mSortedNodes) {
if (mSequenceListener == null) {
mSequenceListener = new SequencerAnimatableListener(this);
if (mSetListener == null) {
mSetListener = new AnimatorSetListener(this);
}
node.animation.addListener(mSequenceListener);
node.animation.addListener(mSetListener);
}
}
if (mSortedNodes.size() > 0) {
@@ -276,9 +272,9 @@ public final class Sequencer extends Animatable {
}
/**
* Returns true if any of the child animations of this Sequencer have been started and have not
* Returns true if any of the child animations of this AnimatorSet have been started and have not
* yet ended.
* @return Whether this Sequencer has been started and has not yet ended.
* @return Whether this AnimatorSet has been started and has not yet ended.
*/
@Override
public boolean isRunning() {
@@ -313,12 +309,12 @@ public final class Sequencer extends Animatable {
}
/**
* Gets the length of each of the child animations of this Sequencer. This value may
* be less than 0, which indicates that no duration has been set on this Sequencer
* Gets the length of each of the child animations of this AnimatorSet. This value may
* be less than 0, which indicates that no duration has been set on this AnimatorSet
* and each of the child animations will use their own duration.
*
* @return The length of the animation, in milliseconds, of each of the child
* animations of this Sequencer.
* animations of this AnimatorSet.
*/
@Override
public long getDuration() {
@@ -326,12 +322,12 @@ public final class Sequencer extends Animatable {
}
/**
* Sets the length of each of the current child animations of this Sequencer. By default,
* each child animation will use its own duration. If the duration is set on the Sequencer,
* Sets the length of each of the current child animations of this AnimatorSet. By default,
* each child animation will use its own duration. If the duration is set on the AnimatorSet,
* then each child animation inherits this duration.
*
* @param duration The length of the animation, in milliseconds, of each of the child
* animations of this Sequencer.
* animations of this AnimatorSet.
*/
@Override
public void setDuration(long duration) {
@@ -339,7 +335,7 @@ public final class Sequencer extends Animatable {
throw new IllegalArgumentException("duration must be a value of zero or greater");
}
for (Node node : mNodes) {
// TODO: don't set the duration of the timing-only nodes created by Sequencer to
// TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
// insert "play-after" delays
node.animation.setDuration(duration);
}
@@ -349,7 +345,7 @@ public final class Sequencer extends Animatable {
/**
* {@inheritDoc}
*
* <p>Starting this <code>Sequencer</code> will, in turn, start the animations for which
* <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
* it is responsible. The details of when exactly those animations are started depends on
* the dependency relationships that have been set up between the animations.
*/
@@ -368,8 +364,8 @@ public final class Sequencer extends Animatable {
// when some other animation also wants to start when the first animation begins.
final ArrayList<Node> nodesToStart = new ArrayList<Node>();
for (Node node : mSortedNodes) {
if (mSequenceListener == null) {
mSequenceListener = new SequencerAnimatableListener(this);
if (mSetListener == null) {
mSetListener = new AnimatorSetListener(this);
}
if (node.dependencies == null || node.dependencies.size() == 0) {
nodesToStart.add(node);
@@ -380,7 +376,7 @@ public final class Sequencer extends Animatable {
}
node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
}
node.animation.addListener(mSequenceListener);
node.animation.addListener(mSetListener);
}
// Now that all dependencies are set up, start the animations that should be started.
if (mStartDelay <= 0) {
@@ -390,9 +386,9 @@ public final class Sequencer extends Animatable {
}
} else {
// TODO: Need to cancel out of the delay appropriately
Animator delayAnim = new Animator(mStartDelay, 0f, 1f);
delayAnim.addListener(new AnimatableListenerAdapter() {
public void onAnimationEnd(Animatable anim) {
ValueAnimator delayAnim = new ValueAnimator(mStartDelay, 0f, 1f);
delayAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
for (Node node : nodesToStart) {
node.animation.start();
mPlayingSet.add(node.animation);
@@ -401,20 +397,20 @@ public final class Sequencer extends Animatable {
});
}
if (mListeners != null) {
ArrayList<AnimatableListener> tmpListeners =
(ArrayList<AnimatableListener>) mListeners.clone();
for (AnimatableListener listener : tmpListeners) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
for (AnimatorListener listener : tmpListeners) {
listener.onAnimationStart(this);
}
}
}
@Override
public Sequencer clone() {
final Sequencer anim = (Sequencer) super.clone();
public AnimatorSet clone() {
final AnimatorSet anim = (AnimatorSet) super.clone();
/*
* The basic clone() operation copies all items. This doesn't work very well for
* Sequencer, because it will copy references that need to be recreated and state
* AnimatorSet, because it will copy references that need to be recreated and state
* that may not apply. What we need to do now is put the clone in an uninitialized
* state, with fresh, empty data structures. Then we will build up the nodes list
* manually, as we clone each Node (and its animation). The clone will then be sorted,
@@ -422,13 +418,13 @@ public final class Sequencer extends Animatable {
*/
anim.mNeedsSort = true;
anim.mCanceled = false;
anim.mPlayingSet = new ArrayList<Animatable>();
anim.mNodeMap = new HashMap<Animatable, Node>();
anim.mPlayingSet = new ArrayList<Animator>();
anim.mNodeMap = new HashMap<Animator, Node>();
anim.mNodes = new ArrayList<Node>();
anim.mSortedNodes = new ArrayList<Node>();
// Walk through the old nodes list, cloning each node and adding it to the new nodemap.
// One problem is that the old node dependencies point to nodes in the old sequencer.
// One problem is that the old node dependencies point to nodes in the old AnimatorSet.
// We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
for (Node node : mNodes) {
@@ -441,21 +437,21 @@ public final class Sequencer extends Animatable {
nodeClone.tmpDependencies = null;
nodeClone.nodeDependents = null;
nodeClone.nodeDependencies = null;
// clear out any listeners that were set up by the sequencer; these will
// clear out any listeners that were set up by the AnimatorSet; these will
// be set up when the clone's nodes are sorted
ArrayList<AnimatableListener> cloneListeners = nodeClone.animation.getListeners();
ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
if (cloneListeners != null) {
ArrayList<AnimatableListener> listenersToRemove = null;
for (AnimatableListener listener : cloneListeners) {
if (listener instanceof SequencerAnimatableListener) {
ArrayList<AnimatorListener> listenersToRemove = null;
for (AnimatorListener listener : cloneListeners) {
if (listener instanceof AnimatorSetListener) {
if (listenersToRemove == null) {
listenersToRemove = new ArrayList<AnimatableListener>();
listenersToRemove = new ArrayList<AnimatorListener>();
}
listenersToRemove.add(listener);
}
}
if (listenersToRemove != null) {
for (AnimatableListener listener : listenersToRemove) {
for (AnimatorListener listener : listenersToRemove) {
cloneListeners.remove(listener);
}
}
@@ -483,9 +479,9 @@ public final class Sequencer extends Animatable {
* animations. If an animation has multiple dependencies on other animations, then
* all dependencies must be satisfied before the animation is started.
*/
private static class DependencyListener implements AnimatableListener {
private static class DependencyListener implements AnimatorListener {
private Sequencer mSequencer;
private AnimatorSet mAnimatorSet;
// The node upon which the dependency is based.
private Node mNode;
@@ -494,8 +490,8 @@ public final class Sequencer extends Animatable {
// the node
private int mRule;
public DependencyListener(Sequencer sequencer, Node node, int rule) {
this.mSequencer = sequencer;
public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
this.mAnimatorSet = animatorSet;
this.mNode = node;
this.mRule = rule;
}
@@ -505,13 +501,13 @@ public final class Sequencer extends Animatable {
* to prevent follow-on animations from running when some dependency
* animation is canceled.
*/
public void onAnimationCancel(Animatable animation) {
public void onAnimationCancel(Animator animation) {
}
/**
* An end event is received - see if this is an event we are listening for
*/
public void onAnimationEnd(Animatable animation) {
public void onAnimationEnd(Animator animation) {
if (mRule == Dependency.AFTER) {
startIfReady(animation);
}
@@ -520,13 +516,13 @@ public final class Sequencer extends Animatable {
/**
* Ignore repeat events for now
*/
public void onAnimationRepeat(Animatable animation) {
public void onAnimationRepeat(Animator animation) {
}
/**
* A start event is received - see if this is an event we are listening for
*/
public void onAnimationStart(Animatable animation) {
public void onAnimationStart(Animator animation) {
if (mRule == Dependency.WITH) {
startIfReady(animation);
}
@@ -538,9 +534,9 @@ public final class Sequencer extends Animatable {
* the animation.
* @param dependencyAnimation the animation that sent the event.
*/
private void startIfReady(Animatable dependencyAnimation) {
if (mSequencer.mCanceled) {
// if the parent Sequencer was canceled, then don't start any dependent anims
private void startIfReady(Animator dependencyAnimation) {
if (mAnimatorSet.mCanceled) {
// if the parent AnimatorSet was canceled, then don't start any dependent anims
return;
}
Dependency dependencyToRemove = null;
@@ -558,37 +554,37 @@ public final class Sequencer extends Animatable {
if (mNode.tmpDependencies.size() == 0) {
// all dependencies satisfied: start the animation
mNode.animation.start();
mSequencer.mPlayingSet.add(mNode.animation);
mAnimatorSet.mPlayingSet.add(mNode.animation);
}
}
}
private class SequencerAnimatableListener implements AnimatableListener {
private class AnimatorSetListener implements AnimatorListener {
private Sequencer mSequencer;
private AnimatorSet mAnimatorSet;
SequencerAnimatableListener(Sequencer sequencer) {
mSequencer = sequencer;
AnimatorSetListener(AnimatorSet animatorSet) {
mAnimatorSet = animatorSet;
}
public void onAnimationCancel(Animatable animation) {
public void onAnimationCancel(Animator animation) {
if (mPlayingSet.size() == 0) {
if (mListeners != null) {
for (AnimatableListener listener : mListeners) {
listener.onAnimationCancel(mSequencer);
for (AnimatorListener listener : mListeners) {
listener.onAnimationCancel(mAnimatorSet);
}
}
}
}
@SuppressWarnings("unchecked")
public void onAnimationEnd(Animatable animation) {
public void onAnimationEnd(Animator animation) {
animation.removeListener(this);
mPlayingSet.remove(animation);
Node animNode = mSequencer.mNodeMap.get(animation);
Node animNode = mAnimatorSet.mNodeMap.get(animation);
animNode.done = true;
ArrayList<Node> sortedNodes = mSequencer.mSortedNodes;
ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
boolean allDone = true;
for (Node node : sortedNodes) {
if (!node.done) {
@@ -598,23 +594,23 @@ public final class Sequencer extends Animatable {
}
if (allDone) {
// If this was the last child animation to end, then notify listeners that this
// sequencer has ended
// AnimatorSet has ended
if (mListeners != null) {
ArrayList<AnimatableListener> tmpListeners =
(ArrayList<AnimatableListener>) mListeners.clone();
for (AnimatableListener listener : tmpListeners) {
listener.onAnimationEnd(mSequencer);
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
for (AnimatorListener listener : tmpListeners) {
listener.onAnimationEnd(mAnimatorSet);
}
}
}
}
// Nothing to do
public void onAnimationRepeat(Animatable animation) {
public void onAnimationRepeat(Animator animation) {
}
// Nothing to do
public void onAnimationStart(Animatable animation) {
public void onAnimationStart(Animator animation) {
}
}
@@ -658,7 +654,7 @@ public final class Sequencer extends Animatable {
mNeedsSort = false;
if (mSortedNodes.size() != mNodes.size()) {
throw new IllegalStateException("Circular dependencies cannot exist"
+ " in Sequencer");
+ " in AnimatorSet");
}
} else {
// Doesn't need sorting, but still need to add in the nodeDependencies list
@@ -702,13 +698,13 @@ public final class Sequencer extends Animatable {
}
/**
* A Node is an embodiment of both the Animatable that it wraps as well as
* A Node is an embodiment of both the Animator that it wraps as well as
* any dependencies that are associated with that Animation. This includes
* both dependencies upon other nodes (in the dependencies list) as
* well as dependencies of other nodes upon this (in the nodeDependents list).
*/
private static class Node implements Cloneable {
public Animatable animation;
public Animator animation;
/**
* These are the dependencies that this node's animation has on other
@@ -723,8 +719,8 @@ public final class Sequencer extends Animatable {
* But we also use the list to keep track of when multiple dependencies are satisfied,
* but removing each dependency as it is satisfied. We do not want to remove
* the dependency itself from the list, because we need to retain that information
* if the sequencer is launched in the future. So we create a copy of the dependency
* list when the sequencer starts and use this tmpDependencies list to track the
* if the AnimatorSet is launched in the future. So we create a copy of the dependency
* list when the AnimatorSet starts and use this tmpDependencies list to track the
* list of satisfied dependencies.
*/
public ArrayList<Dependency> tmpDependencies = null;
@@ -744,8 +740,8 @@ public final class Sequencer extends Animatable {
/**
* Flag indicating whether the animation in this node is finished. This flag
* is used by Sequencer to check, as each animation ends, whether all child animations
* are done and it's time to send out an end event for the entire Sequencer.
* is used by AnimatorSet to check, as each animation ends, whether all child animations
* are done and it's time to send out an end event for the entire AnimatorSet.
*/
public boolean done = false;
@@ -756,7 +752,7 @@ public final class Sequencer extends Animatable {
*
* @param animation The animation that the Node encapsulates.
*/
public Node(Animatable animation) {
public Node(Animator animation) {
this.animation = animation;
}
@@ -785,7 +781,7 @@ public final class Sequencer extends Animatable {
public Node clone() {
try {
Node node = (Node) super.clone();
node.animation = (Animatable) animation.clone();
node.animation = (Animator) animation.clone();
return node;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
@@ -795,45 +791,45 @@ public final class Sequencer extends Animatable {
/**
* The <code>Builder</code> object is a utility class to facilitate adding animations to a
* <code>Sequencer</code> along with the relationships between the various animations. The
* <code>AnimatorSet</code> along with the relationships between the various animations. The
* intention of the <code>Builder</code> methods, along with the {@link
* Sequencer#play(Animatable) play()} method of <code>Sequencer</code> is to make it possible to
* AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible to
* express the dependency relationships of animations in a natural way. Developers can also use
* the {@link Sequencer#playTogether(Animatable[]) playTogether()} and {@link
* Sequencer#playSequentially(Animatable[]) playSequentially()} methods if these suit the need,
* but it might be easier in some situations to express the sequence of animations in pairs.
* the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
* AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
* but it might be easier in some situations to express the AnimatorSet of animations in pairs.
* <p/>
* <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
* internally via a call to {@link Sequencer#play(Animatable)}.</p>
* internally via a call to {@link AnimatorSet#play(Animator)}.</p>
* <p/>
* <p>For example, this sets up a Sequencer to play anim1 and anim2 at the same time, anim3 to
* <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
* play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
* <pre>
* Sequencer s = new Sequencer();
* AnimatorSet s = new AnimatorSet();
* s.play(anim1).with(anim2);
* s.play(anim2).before(anim3);
* s.play(anim4).after(anim3);
* </pre>
* <p/>
* <p>Note in the example that both {@link Builder#before(Animatable)} and {@link
* Builder#after(Animatable)} are used. These are just different ways of expressing the same
* <p>Note in the example that both {@link Builder#before(Animator)} and {@link
* Builder#after(Animator)} are used. These are just different ways of expressing the same
* relationship and are provided to make it easier to say things in a way that is more natural,
* depending on the situation.</p>
* <p/>
* <p>It is possible to make several calls into the same <code>Builder</code> object to express
* multiple relationships. However, note that it is only the animation passed into the initial
* {@link Sequencer#play(Animatable)} method that is the dependency in any of the successive
* {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
* calls to the <code>Builder</code> object. For example, the following code starts both anim2
* and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
* anim3:
* <pre>
* Sequencer s = new Sequencer();
* AnimatorSet s = new AnimatorSet();
* s.play(anim1).before(anim2).before(anim3);
* </pre>
* If the desired result is to play anim1 then anim2 then anim3, this code expresses the
* relationship correctly:</p>
* <pre>
* Sequencer s = new Sequencer();
* AnimatorSet s = new AnimatorSet();
* s.play(anim1).before(anim2);
* s.play(anim2).before(anim3);
* </pre>
@@ -841,26 +837,26 @@ public final class Sequencer extends Animatable {
* <p>Note that it is possible to express relationships that cannot be resolved and will not
* result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
* sense. In general, circular dependencies like this one (or more indirect ones where a depends
* on b, which depends on c, which depends on a) should be avoided. Only create sequences that
* can boil down to a simple, one-way relationship of animations starting with, before, and
* on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
* that can boil down to a simple, one-way relationship of animations starting with, before, and
* after other, different, animations.</p>
*/
public class Builder {
/**
* This tracks the current node being processed. It is supplied to the play() method
* of Sequencer and passed into the constructor of Builder.
* of AnimatorSet and passed into the constructor of Builder.
*/
private Node mCurrentNode;
/**
* package-private constructor. Builders are only constructed by Sequencer, when the
* package-private constructor. Builders are only constructed by AnimatorSet, when the
* play() method is called.
*
* @param anim The animation that is the dependency for the other animations passed into
* the other methods of this Builder object.
*/
Builder(Animatable anim) {
Builder(Animator anim) {
mCurrentNode = mNodeMap.get(anim);
if (mCurrentNode == null) {
mCurrentNode = new Node(anim);
@@ -871,12 +867,12 @@ public final class Sequencer extends Animatable {
/**
* Sets up the given animation to play at the same time as the animation supplied in the
* {@link Sequencer#play(Animatable)} call that created this <code>Builder</code> object.
* {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
*
* @param anim The animation that will play when the animation supplied to the
* {@link Sequencer#play(Animatable)} method starts.
* {@link AnimatorSet#play(Animator)} method starts.
*/
public void with(Animatable anim) {
public void with(Animator anim) {
Node node = mNodeMap.get(anim);
if (node == null) {
node = new Node(anim);
@@ -889,13 +885,13 @@ public final class Sequencer extends Animatable {
/**
* Sets up the given animation to play when the animation supplied in the
* {@link Sequencer#play(Animatable)} call that created this <code>Builder</code> object
* {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
* ends.
*
* @param anim The animation that will play when the animation supplied to the
* {@link Sequencer#play(Animatable)} method ends.
* {@link AnimatorSet#play(Animator)} method ends.
*/
public void before(Animatable anim) {
public void before(Animator anim) {
Node node = mNodeMap.get(anim);
if (node == null) {
node = new Node(anim);
@@ -908,13 +904,13 @@ public final class Sequencer extends Animatable {
/**
* Sets up the given animation to play when the animation supplied in the
* {@link Sequencer#play(Animatable)} call that created this <code>Builder</code> object
* {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
* to start when the animation supplied in this method call ends.
*
* @param anim The animation whose end will cause the animation supplied to the
* {@link Sequencer#play(Animatable)} method to play.
* {@link AnimatorSet#play(Animator)} method to play.
*/
public void after(Animatable anim) {
public void after(Animator anim) {
Node node = mNodeMap.get(anim);
if (node == null) {
node = new Node(anim);
@@ -927,15 +923,15 @@ public final class Sequencer extends Animatable {
/**
* Sets up the animation supplied in the
* {@link Sequencer#play(Animatable)} call that created this <code>Builder</code> object
* {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
* to play when the given amount of time elapses.
*
* @param delay The number of milliseconds that should elapse before the
* animation starts.
*/
public void after(long delay) {
// setup dummy Animator just to run the clock
after(new Animator(delay, 0f, 1f));
// setup dummy ValueAnimator just to run the clock
after(new ValueAnimator(delay, 0f, 1f));
}
}

View File

@@ -20,7 +20,7 @@ import android.view.animation.Interpolator;
/**
* This class holds a time/value pair for an animation. The Keyframe class is used
* by {@link Animator} to define the values that the animation target will have over the course
* by {@link ValueAnimator} to define the values that the animation target will have over the course
* of the animation. As the time proceeds from one keyframe to the other, the value of the
* target object will animate between the value at the previous keyframe and the value at the
* next keyframe. Each keyframe also holds an option {@link android.view.animation.Interpolator}
@@ -59,7 +59,7 @@ public class Keyframe implements Cloneable {
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
* @param valueType The type of the <code>value</code> object. This is used by the
* {@link #getValue()} functionm, which is queried by {@link Animator} to determine
* {@link #getValue()} functionm, which is queried by {@link ValueAnimator} to determine
* the type of {@link TypeEvaluator} to use to interpolate between values.
*/
private Keyframe(float fraction, Object value, Class valueType) {
@@ -239,7 +239,7 @@ public class Keyframe implements Cloneable {
}
/**
* Gets the type of keyframe. This information is used by Animator to determine the type of
* Gets the type of keyframe. This information is used by ValueAnimator to determine the type of
* {@link TypeEvaluator} to use when calculating values between keyframes. The type is based
* on the type of Keyframe created.
*

View File

@@ -21,7 +21,7 @@ import java.util.ArrayList;
import android.view.animation.Interpolator;
/**
* This class holds a collection of Keyframe objects and is called by Animator to calculate
* This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
*/

View File

@@ -32,8 +32,8 @@ import java.util.List;
* transitions for a layout container, create a LayoutTransition object and set it on any
* ViewGroup by calling {@link ViewGroup#setLayoutTransition(LayoutTransition)}. This will cause
* default animations to run whenever items are added to or removed from that container. To specify
* custom animations, use the {@link LayoutTransition#setAnimatable(int, Animatable)
* setAnimatable()} method.
* custom animations, use the {@link LayoutTransition#setAnimator(int, Animator)
* setAnimator()} method.
*
* <p>One of the core concepts of these transition animations is that there are two core
* changes that cause the transition and four different animations that run because of
@@ -61,7 +61,7 @@ import java.util.List;
* CHANGE_APPEARING animation animates <code>left</code>, <code>top</code>, <code>right</code>,
* and <code>bottom</code>. Values for these properties are updated with the pre- and post-layout
* values when the transition begins. Custom animations will be similarly populated with
* the target and values being animated, assuming they use PropertyAnimator objects with
* the target and values being animated, assuming they use ObjectAnimator objects with
* property names that are known on the target object.</p>
*/
public class LayoutTransition {
@@ -93,21 +93,21 @@ public class LayoutTransition {
/**
* These variables hold the animations that are currently used to run the transition effects.
* These animations are set to defaults, but can be changed to custom animations by
* calls to setAnimatable().
* calls to setAnimator().
*/
private Animatable mDisappearingAnim = null;
private Animatable mAppearingAnim = null;
private Animatable mChangingAppearingAnim = null;
private Animatable mChangingDisappearingAnim = null;
private Animator mDisappearingAnim = null;
private Animator mAppearingAnim = null;
private Animator mChangingAppearingAnim = null;
private Animator mChangingDisappearingAnim = null;
/**
* These are the default animations, defined in the constructor, that will be used
* unless the user specifies custom animations.
*/
private static PropertyAnimator defaultChangeIn;
private static PropertyAnimator defaultChangeOut;
private static PropertyAnimator defaultFadeIn;
private static PropertyAnimator defaultFadeOut;
private static ObjectAnimator defaultChangeIn;
private static ObjectAnimator defaultChangeOut;
private static ObjectAnimator defaultFadeIn;
private static ObjectAnimator defaultFadeOut;
/**
* The default duration used by all animations.
@@ -154,7 +154,7 @@ public class LayoutTransition {
* we cache all of the current animations in this map for possible cancellation on
* another layout event.
*/
private HashMap<View, Animatable> currentAnimations = new HashMap<View, Animatable>();
private HashMap<View, Animator> currentAnimations = new HashMap<View, Animator>();
/**
* This hashmap is used to track the listeners that have been added to the children of
@@ -194,7 +194,7 @@ public class LayoutTransition {
PropertyValuesHolder<Integer> pvhTop = new PropertyValuesHolder<Integer>("top", 0, 1);
PropertyValuesHolder<Integer> pvhRight = new PropertyValuesHolder<Integer>("right", 0, 1);
PropertyValuesHolder<Integer> pvhBottom = new PropertyValuesHolder<Integer>("bottom", 0, 1);
defaultChangeIn = new PropertyAnimator<PropertyValuesHolder>(DEFAULT_DURATION, this,
defaultChangeIn = new ObjectAnimator<PropertyValuesHolder>(DEFAULT_DURATION, this,
pvhLeft, pvhTop, pvhRight, pvhBottom);
defaultChangeIn.setStartDelay(mChangingAppearingDelay);
defaultChangeIn.setInterpolator(mChangingAppearingInterpolator);
@@ -202,11 +202,11 @@ public class LayoutTransition {
defaultChangeOut.setStartDelay(mChangingDisappearingDelay);
defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator);
defaultFadeIn =
new PropertyAnimator<Float>(DEFAULT_DURATION, this, "alpha", 0f, 1f);
new ObjectAnimator<Float>(DEFAULT_DURATION, this, "alpha", 0f, 1f);
defaultFadeIn.setStartDelay(mAppearingDelay);
defaultFadeIn.setInterpolator(mAppearingInterpolator);
defaultFadeOut =
new PropertyAnimator<Float>(DEFAULT_DURATION, this, "alpha", 1f, 0f);
new ObjectAnimator<Float>(DEFAULT_DURATION, this, "alpha", 1f, 0f);
defaultFadeOut.setStartDelay(mDisappearingDelay);
defaultFadeOut.setInterpolator(mDisappearingInterpolator);
}
@@ -240,7 +240,7 @@ public class LayoutTransition {
* {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose start
* delay is being set.
* @param delay The length of time, in milliseconds, to delay before starting the animation.
* @see android.animation.Animatable#setStartDelay(long)
* @see Animator#setStartDelay(long)
*/
public void setStartDelay(int transitionType, long delay) {
switch (transitionType) {
@@ -268,7 +268,7 @@ public class LayoutTransition {
* {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose start
* delay is returned.
* @return long The start delay of the specified animation.
* @see android.animation.Animatable#getStartDelay()
* @see Animator#getStartDelay()
*/
public long getStartDelay(int transitionType) {
switch (transitionType) {
@@ -294,7 +294,7 @@ public class LayoutTransition {
* {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
* duration is being set.
* @param duration The length of time, in milliseconds, that the specified animation should run.
* @see android.animation.Animatable#setDuration(long)
* @see Animator#setDuration(long)
*/
public void setDuration(int transitionType, long duration) {
switch (transitionType) {
@@ -322,7 +322,7 @@ public class LayoutTransition {
* {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
* duration is returned.
* @return long The duration of the specified animation.
* @see android.animation.Animatable#getDuration()
* @see Animator#getDuration()
*/
public long getDuration(int transitionType) {
switch (transitionType) {
@@ -387,7 +387,7 @@ public class LayoutTransition {
* {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
* duration is being set.
* @param interpolator The interpolator that the specified animation should use.
* @see android.animation.Animatable#setInterpolator(android.view.animation.Interpolator)
* @see Animator#setInterpolator(android.view.animation.Interpolator)
*/
public void setInterpolator(int transitionType, Interpolator interpolator) {
switch (transitionType) {
@@ -415,7 +415,7 @@ public class LayoutTransition {
* {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
* duration is being set.
* @return Interpolator The interpolator that the specified animation uses.
* @see android.animation.Animatable#setInterpolator(android.view.animation.Interpolator)
* @see Animator#setInterpolator(android.view.animation.Interpolator)
*/
public Interpolator getInterpolator(int transitionType) {
switch (transitionType) {
@@ -434,11 +434,11 @@ public class LayoutTransition {
/**
* Sets the animation used during one of the transition types that may run. Any
* Animatable object can be used, but to be most useful in the context of layout
* transitions, the animation should either be a PropertyAnimator or a Sequencer
* of animations including PropertyAnimators. Also, these PropertyAnimator objects
* Animator object can be used, but to be most useful in the context of layout
* transitions, the animation should either be a ObjectAnimator or a AnimatorSet
* of animations including PropertyAnimators. Also, these ObjectAnimator objects
* should be able to get and set values on their target objects automatically. For
* example, a PropertyAnimator that animates the property "left" is able to set and get the
* example, a ObjectAnimator that animates the property "left" is able to set and get the
* <code>left</code> property from the View objects being animated by the layout
* transition. The transition works by setting target objects and properties
* dynamically, according to the pre- and post-layoout values of those objects, so
@@ -454,26 +454,26 @@ public class LayoutTransition {
* object (presumably 1) as its starting and ending value when the animation begins.
* Animations which need to use values at the beginning and end that may not match the
* values queried when the transition begins may need to use a different mechanism
* than a standard PropertyAnimator object.</p>
* than a standard ObjectAnimator object.</p>
*
* @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
* {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
* duration is being set.
* @param animatable The animation being assigned.
* @param animator The animation being assigned.
*/
public void setAnimatable(int transitionType, Animatable animatable) {
public void setAnimator(int transitionType, Animator animator) {
switch (transitionType) {
case CHANGE_APPEARING:
mChangingAppearingAnim = (animatable != null) ? animatable : defaultChangeIn;
mChangingAppearingAnim = (animator != null) ? animator : defaultChangeIn;
break;
case CHANGE_DISAPPEARING:
mChangingDisappearingAnim = (animatable != null) ? animatable : defaultChangeOut;
mChangingDisappearingAnim = (animator != null) ? animator : defaultChangeOut;
break;
case APPEARING:
mAppearingAnim = (animatable != null) ? animatable : defaultFadeIn;
mAppearingAnim = (animator != null) ? animator : defaultFadeIn;
break;
case DISAPPEARING:
mDisappearingAnim = (animatable != null) ? animatable : defaultFadeOut;
mDisappearingAnim = (animator != null) ? animator : defaultFadeOut;
break;
}
}
@@ -484,10 +484,10 @@ public class LayoutTransition {
* @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
* {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
* duration is being set.
* @return Animatable The animation being used for the given transition type.
* @see #setAnimatable(int, Animatable)
* @return Animator The animation being used for the given transition type.
* @see #setAnimator(int, Animator)
*/
public Animatable getAnimatable(int transitionType) {
public Animator getAnimator(int transitionType) {
switch (transitionType) {
case CHANGE_APPEARING:
return mChangingAppearingAnim;
@@ -529,21 +529,21 @@ public class LayoutTransition {
if (child != newView) {
// If there's an animation running on this view already, cancel it
Animatable currentAnimation = currentAnimations.get(child);
Animator currentAnimation = currentAnimations.get(child);
if (currentAnimation != null) {
currentAnimation.cancel();
currentAnimations.remove(child);
}
// Make a copy of the appropriate animation
final Animatable anim = (changeReason == APPEARING) ?
final Animator anim = (changeReason == APPEARING) ?
mChangingAppearingAnim.clone() :
mChangingDisappearingAnim.clone();
// Set the target object for the animation
anim.setTarget(child);
// A PropertyAnimator (or Sequencer of them) can extract start values from
// A ObjectAnimator (or AnimatorSet of them) can extract start values from
// its target object
anim.setupStartValues();
@@ -574,20 +574,20 @@ public class LayoutTransition {
anim.setDuration(duration);
// Remove the animation from the cache when it ends
anim.addListener(new AnimatableListenerAdapter() {
anim.addListener(new AnimatorListenerAdapter() {
private boolean canceled = false;
public void onAnimationCancel(Animatable animatable) {
public void onAnimationCancel(Animator animator) {
// we remove canceled animations immediately, not here
canceled = true;
}
public void onAnimationEnd(Animatable animatable) {
public void onAnimationEnd(Animator animator) {
if (!canceled) {
currentAnimations.remove(child);
}
}
});
if (anim instanceof PropertyAnimator) {
((PropertyAnimator) anim).setCurrentPlayTime(0);
if (anim instanceof ObjectAnimator) {
((ObjectAnimator) anim).setCurrentPlayTime(0);
}
anim.start();
@@ -626,15 +626,15 @@ public class LayoutTransition {
* @param child The View being added to the ViewGroup.
*/
private void runAppearingTransition(final ViewGroup parent, final View child) {
Animatable anim = mAppearingAnim.clone();
Animator anim = mAppearingAnim.clone();
anim.setTarget(child);
anim.setStartDelay(mAppearingDelay);
anim.setDuration(mAppearingDuration);
if (anim instanceof PropertyAnimator) {
((PropertyAnimator) anim).setCurrentPlayTime(0);
if (anim instanceof ObjectAnimator) {
((ObjectAnimator) anim).setCurrentPlayTime(0);
}
if (mListeners != null) {
anim.addListener(new AnimatableListenerAdapter() {
anim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd() {
for (TransitionListener listener : mListeners) {
listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
@@ -652,12 +652,12 @@ public class LayoutTransition {
* @param child The View being removed from the ViewGroup.
*/
private void runDisappearingTransition(final ViewGroup parent, final View child) {
Animatable anim = mDisappearingAnim.clone();
Animator anim = mDisappearingAnim.clone();
anim.setStartDelay(mDisappearingDelay);
anim.setDuration(mDisappearingDuration);
anim.setTarget(child);
if (mListeners != null) {
anim.addListener(new AnimatableListenerAdapter() {
anim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd() {
for (TransitionListener listener : mListeners) {
listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
@@ -665,8 +665,8 @@ public class LayoutTransition {
}
});
}
if (anim instanceof PropertyAnimator) {
((PropertyAnimator) anim).setCurrentPlayTime(0);
if (anim instanceof ObjectAnimator) {
((ObjectAnimator) anim).setCurrentPlayTime(0);
}
anim.start();
}

View File

@@ -21,13 +21,13 @@ import android.util.Log;
import java.lang.reflect.Method;
/**
* This subclass of {@link Animator} provides support for animating properties on target objects.
* This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
* The constructors of this class take parameters to define the target object that will be animated
* as well as the name of the property that will be animated. Appropriate set/get functions
* are then determined internally and the animation will call these functions as necessary to
* animate the property.
*/
public final class PropertyAnimator<T> extends Animator<T> {
public final class ObjectAnimator<T> extends ValueAnimator<T> {
// The target object on which the property exists, set in the constructor
private Object mTarget;
@@ -47,7 +47,7 @@ public final class PropertyAnimator<T> extends Animator<T> {
* <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
* the setter function will fail.</p>
*
* <p>If this PropertyAnimator has been set up to animate several properties together,
* <p>If this ObjectAnimator has been set up to animate several properties together,
* using more than one PropertyValuesHolder objects, then setting the propertyName simply
* sets the propertyName in the first of those PropertyValuesHolder objects.</p>
*
@@ -101,18 +101,18 @@ public final class PropertyAnimator<T> extends Animator<T> {
try {
returnVal = mTarget.getClass().getMethod(setterName, args);
} catch (NoSuchMethodException e) {
Log.e("PropertyAnimator",
Log.e("ObjectAnimator",
"Couldn't find setter/getter for property " + mPropertyName + ": " + e);
}
return returnVal;
}
/**
* Creates a new PropertyAnimator object. This default constructor is primarily for
* Creates a new ObjectAnimator object. This default constructor is primarily for
* use internally; the other constructors which take parameters are more generally
* useful.
*/
public PropertyAnimator() {
public ObjectAnimator() {
}
/**
@@ -128,7 +128,7 @@ public final class PropertyAnimator<T> extends Animator<T> {
* is assumed to be the final value being animated to, and the initial value will be
* derived on the fly.
*/
public PropertyAnimator(long duration, Object target, String propertyName, T...values) {
public ObjectAnimator(long duration, Object target, String propertyName, T...values) {
super(duration, (T[]) values);
mTarget = target;
setPropertyName(propertyName);
@@ -136,7 +136,7 @@ public final class PropertyAnimator<T> extends Animator<T> {
/**
* A constructor that takes <code>PropertyValueHolder</code> values. This constructor should
* be used when animating several properties at once with the same PropertyAnimator, since
* be used when animating several properties at once with the same ObjectAnimator, since
* PropertyValuesHolder allows you to associate a set of animation values with a property
* name.
*
@@ -148,7 +148,7 @@ public final class PropertyAnimator<T> extends Animator<T> {
* @param values The PropertyValuesHolder objects which hold each the property name and values
* to animate that property between.
*/
public PropertyAnimator(long duration, Object target, PropertyValuesHolder...values) {
public ObjectAnimator(long duration, Object target, PropertyValuesHolder...values) {
super(duration);
setValues(values);
mTarget = target;
@@ -239,8 +239,8 @@ public final class PropertyAnimator<T> extends Animator<T> {
}
@Override
public PropertyAnimator clone() {
final PropertyAnimator anim = (PropertyAnimator) super.clone();
public ObjectAnimator clone() {
final ObjectAnimator anim = (ObjectAnimator) super.clone();
return anim;
}
}

View File

@@ -27,31 +27,31 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* This class holds information about a property and the values that that property
* should take on during an animation. PropertyValuesHolder objects can be used to create
* animations with Animator or PropertyAnimator that operate on several different properties
* animations with ValueAnimator or ObjectAnimator that operate on several different properties
* in parallel.
*/
public class PropertyValuesHolder<T> implements Cloneable {
/**
* The name of the property associated with the values. This need not be a real property,
* unless this object is being used with PropertyAnimator. But this is the name by which
* aniamted values are looked up with getAnimatedValue(String) in Animator.
* unless this object is being used with ObjectAnimator. But this is the name by which
* aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
*/
private String mPropertyName;
/**
* The setter function, if needed. PropertyAnimator hands off this functionality to
* The setter function, if needed. ObjectAnimator hands off this functionality to
* PropertyValuesHolder, since it holds all of the per-property information. This
* property can be manually set via setSetter(). Otherwise, it is automatically
* derived when the animation starts in setupSetterAndGetter() if using PropertyAnimator.
* derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
*/
private Method mSetter = null;
/**
* The getter function, if needed. PropertyAnimator hands off this functionality to
* The getter function, if needed. ObjectAnimator hands off this functionality to
* PropertyValuesHolder, since it holds all of the per-property information. This
* property can be manually set via setSetter(). Otherwise, it is automatically
* derived when the animation starts in setupSetterAndGetter() if using PropertyAnimator.
* derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
* The getter is only derived and used if one of the values is null.
*/
private Method mGetter = null;
@@ -112,16 +112,16 @@ public class PropertyValuesHolder<T> implements Cloneable {
/**
* The value most recently calculated by calculateValue(). This is set during
* that function and might be retrieved later either by Animator.animatedValue() or
* by the property-setting logic in PropertyAnimator.animatedValue().
* that function and might be retrieved later either by ValueAnimator.animatedValue() or
* by the property-setting logic in ObjectAnimator.animatedValue().
*/
private Object mAnimatedValue;
/**
* Constructs a PropertyValuesHolder object with just a set of values. This constructor
* is typically not used when animating objects with PropertyAnimator, because that
* is typically not used when animating objects with ObjectAnimator, because that
* object needs distinct and meaningful property names. Simpler animations of one
* set of values using Animator may use this constructor, however, because no
* set of values using ValueAnimator may use this constructor, however, because no
* distinguishing name is needed.
* @param values The set of values to animate between. If there is only one value, it
* is assumed to be the final value being animated to, and the initial value will be
@@ -141,14 +141,14 @@ public class PropertyValuesHolder<T> implements Cloneable {
* on the object. Also, if any value is null, the value will be filled in when the animation
* starts in the same way. This mechanism of automatically getting null values only works
* if the PropertyValuesHolder object is used in conjunction
* {@link android.animation.PropertyAnimator}, and with a getter function either
* {@link ObjectAnimator}, and with a getter function either
* derived automatically from <code>propertyName</code> or set explicitly via
* {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
* no way of determining what the value should be.
* @param propertyName The name of the property associated with this set of values. This
* can be the actual property name to be used when using a PropertyAnimator object, or
* can be the actual property name to be used when using a ObjectAnimator object, or
* just a name used to get animated values, such as if this object is used with an
* Animator object.
* ValueAnimator object.
* @param values The set of values to animate between.
*/
public PropertyValuesHolder(String propertyName, T... values) {
@@ -163,7 +163,7 @@ public class PropertyValuesHolder<T> implements Cloneable {
* on the object. Also, if any value is null, the value will be filled in when the animation
* starts in the same way. This mechanism of automatically getting null values only works
* if the PropertyValuesHolder object is used in conjunction
* {@link android.animation.PropertyAnimator}, and with a getter function either
* {@link ObjectAnimator}, and with a getter function either
* derived automatically from <code>propertyName</code> or set explicitly via
* {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
* no way of determining what the value should be.
@@ -331,7 +331,7 @@ public class PropertyValuesHolder<T> implements Cloneable {
}
/**
* Internal function (called from PropertyAnimator) to set up the setter and getter
* Internal function (called from ObjectAnimator) to set up the setter and getter
* prior to running the animation. If the setter has not been manually set for this
* object, it will be derived automatically given the property name, target object, and
* types of values supplied. If no getter has been set, it will be supplied iff any of the
@@ -383,7 +383,7 @@ public class PropertyValuesHolder<T> implements Cloneable {
}
/**
* This function is called by PropertyAnimator when setting the start values for an animation.
* This function is called by ObjectAnimator when setting the start values for an animation.
* The start values are set according to the current values in the target object. The
* property whose value is extracted is whatever is specified by the propertyName of this
* PropertyValuesHolder object.
@@ -395,7 +395,7 @@ public class PropertyValuesHolder<T> implements Cloneable {
}
/**
* This function is called by PropertyAnimator when setting the end values for an animation.
* This function is called by ObjectAnimator when setting the end values for an animation.
* The end values are set according to the current values in the target object. The
* property whose value is extracted is whatever is specified by the propertyName of this
* PropertyValuesHolder object.
@@ -420,8 +420,8 @@ public class PropertyValuesHolder<T> implements Cloneable {
}
/**
* Internal function to set the value on the target object, using the setter set up
* earlier on this PropertyValuesHolder object. This function is called by PropertyAnimator
* to handle turning the value calculated by Animator into a value set on the object
* earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
* to handle turning the value calculated by ValueAnimator into a value set on the object
* according to the name of the property.
* @param target The target object on which the value is set
*/
@@ -439,7 +439,7 @@ public class PropertyValuesHolder<T> implements Cloneable {
}
/**
* Internal function, called by Animator, to set up the TypeEvaluator that will be used
* Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
* to calculate animated values.
*/
void init() {
@@ -466,7 +466,7 @@ public class PropertyValuesHolder<T> implements Cloneable {
/**
* Function used to calculate the value according to the evaluator set up for
* this PropertyValuesHolder object. This function is called by Animator.animateValue().
* this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
*
* @param fraction The elapsed, interpolated fraction of the animation.
* @return The calculated value at this point in the animation.
@@ -483,7 +483,7 @@ public class PropertyValuesHolder<T> implements Cloneable {
* approach is more direct, and is especially useful when a function must be called that does
* not correspond to the convention of <code>setName()</code>. For example, if a function
* called <code>offset()</code> is to be called with the animated values, there is no way
* to tell <code>PropertyAnimator</code> how to call that function simply through a property
* to tell <code>ObjectAnimator</code> how to call that function simply through a property
* name, so a setter method should be supplied instead.
*
* <p>Note that the setter function must take the same parameter type as the
@@ -511,7 +511,7 @@ public class PropertyValuesHolder<T> implements Cloneable {
* approach is more direct, and is especially useful when a function must be called that does
* not correspond to the convention of <code>setName()</code>. For example, if a function
* called <code>offset()</code> is to be called to get an initial value, there is no way
* to tell <code>PropertyAnimator</code> how to call that function simply through a property
* to tell <code>ObjectAnimator</code> how to call that function simply through a property
* name, so a getter method should be supplied instead.
*
* <p>Note that the getter method is only called whether supplied here or derived
@@ -569,7 +569,7 @@ public class PropertyValuesHolder<T> implements Cloneable {
}
/**
* Internal function, called by Animator and PropertyAnimator, to retrieve the value
* Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
* most recently calculated in calculateValue().
* @return
*/

View File

@@ -17,12 +17,12 @@
package android.animation;
/**
* Interface for use with the {@link Animator#setEvaluator(TypeEvaluator)} function. Evaluators
* Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
* allow developers to create animations on arbitrary property types, by allowing them to supply
* custom evaulators for types that are not automatically understood and used by the animation
* system.
*
* @see Animator#setEvaluator(TypeEvaluator)
* @see ValueAnimator#setEvaluator(TypeEvaluator)
*/
public interface TypeEvaluator {

View File

@@ -0,0 +1,972 @@
/*
* Copyright (C) 2010 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.animation;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import java.util.ArrayList;
import java.util.HashMap;
/**
* This class provides a simple timing engine for running animations
* which calculate animated values and set them on target objects.
*
* <p>There is a single timing pulse that all animations use. It runs in a
* custom handler to ensure that property changes happen on the UI thread.</p>
*
* <p>By default, ValueAnimator uses non-linear time interpolation, via the
* {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates
* out of an animation. This behavior can be changed by calling
* {@link ValueAnimator#setInterpolator(Interpolator)}.</p>
*/
public class ValueAnimator<T> extends Animator {
/**
* Internal constants
*/
/*
* The default amount of time in ms between animation frames
*/
private static final long DEFAULT_FRAME_DELAY = 30;
/**
* Messages sent to timing handler: START is sent when an animation first begins, FRAME is sent
* by the handler to itself to process the next animation frame
*/
private static final int ANIMATION_START = 0;
private static final int ANIMATION_FRAME = 1;
/**
* Values used with internal variable mPlayingState to indicate the current state of an
* animation.
*/
private static final int STOPPED = 0; // Not yet playing
private static final int RUNNING = 1; // Playing normally
private static final int CANCELED = 2; // cancel() called - need to end it
private static final int ENDED = 3; // end() called - need to end it
private static final int SEEKED = 4; // Seeked to some time value
/**
* Internal variables
* NOTE: This object implements the clone() method, making a deep copy of any referenced
* objects. As other non-trivial fields are added to this class, make sure to add logic
* to clone() to make deep copies of them.
*/
// The first time that the animation's animateFrame() method is called. This time is used to
// determine elapsed time (and therefore the elapsed fraction) in subsequent calls
// to animateFrame()
private long mStartTime;
/**
* Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
* to a value.
*/
private long mSeekTime = -1;
// The static sAnimationHandler processes the internal timing loop on which all animations
// are based
private static AnimationHandler sAnimationHandler;
// The static list of all active animations
private static final ArrayList<ValueAnimator> sAnimations = new ArrayList<ValueAnimator>();
// The set of animations to be started on the next animation frame
private static final ArrayList<ValueAnimator> sPendingAnimations = new ArrayList<ValueAnimator>();
// The time interpolator to be used if none is set on the animation
private static final Interpolator sDefaultInterpolator = new AccelerateDecelerateInterpolator();
// type evaluators for the three primitive types handled by this implementation
private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
private static final TypeEvaluator sDoubleEvaluator = new DoubleEvaluator();
/**
* Used to indicate whether the animation is currently playing in reverse. This causes the
* elapsed fraction to be inverted to calculate the appropriate values.
*/
private boolean mPlayingBackwards = false;
/**
* This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the
* repeatCount (if repeatCount!=INFINITE), the animation ends
*/
private int mCurrentIteration = 0;
/**
* Tracks whether a startDelay'd animation has begun playing through the startDelay.
*/
private boolean mStartedDelay = false;
/**
* Tracks the time at which the animation began playing through its startDelay. This is
* different from the mStartTime variable, which is used to track when the animation became
* active (which is when the startDelay expired and the animation was added to the active
* animations list).
*/
private long mDelayStartTime;
/**
* Flag that represents the current state of the animation. Used to figure out when to start
* an animation (if state == STOPPED). Also used to end an animation that
* has been cancel()'d or end()'d since the last animation frame. Possible values are
* STOPPED, RUNNING, ENDED, CANCELED.
*/
private int mPlayingState = STOPPED;
/**
* Internal collections used to avoid set collisions as animations start and end while being
* processed.
*/
private static final ArrayList<ValueAnimator> sEndingAnims = new ArrayList<ValueAnimator>();
private static final ArrayList<ValueAnimator> sDelayedAnims = new ArrayList<ValueAnimator>();
private static final ArrayList<ValueAnimator> sReadyAnims = new ArrayList<ValueAnimator>();
/**
* Flag that denotes whether the animation is set up and ready to go. Used to
* set up animation that has not yet been started.
*/
boolean mInitialized = false;
//
// Backing variables
//
// How long the animation should last in ms
private long mDuration;
// The amount of time in ms to delay starting the animation after start() is called
private long mStartDelay = 0;
// The number of milliseconds between animation frames
private static long sFrameDelay = DEFAULT_FRAME_DELAY;
// The number of times the animation will repeat. The default is 0, which means the animation
// will play only once
private int mRepeatCount = 0;
/**
* The type of repetition that will occur when repeatMode is nonzero. RESTART means the
* animation will start from the beginning on every new cycle. REVERSE means the animation
* will reverse directions on each iteration.
*/
private int mRepeatMode = RESTART;
/**
* The time interpolator to be used. The elapsed fraction of the animation will be passed
* through this interpolator to calculate the interpolated fraction, which is then used to
* calculate the animated values.
*/
private Interpolator mInterpolator = sDefaultInterpolator;
/**
* The set of listeners to be sent events through the life of an animation.
*/
private ArrayList<AnimatorUpdateListener> mUpdateListeners = null;
/**
* The property/value sets being animated.
*/
PropertyValuesHolder[] mValues;
/**
* A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values
* by property name during calls to getAnimatedValue(String).
*/
HashMap<String, PropertyValuesHolder> mValuesMap;
/**
* Public constants
*/
/**
* When the animation reaches the end and <code>repeatCount</code> is INFINITE
* or a positive value, the animation restarts from the beginning.
*/
public static final int RESTART = 1;
/**
* When the animation reaches the end and <code>repeatCount</code> is INFINITE
* or a positive value, the animation reverses direction on every iteration.
*/
public static final int REVERSE = 2;
/**
* This value used used with the {@link #setRepeatCount(int)} property to repeat
* the animation indefinitely.
*/
public static final int INFINITE = -1;
/**
* Creates a new ValueAnimator object. This default constructor is primarily for
* use internally; the other constructors which take parameters are more generally
* useful.
*/
public ValueAnimator() {
}
/**
* Constructs an ValueAnimator object with the specified duration and set of
* values. If the values are a set of PropertyValuesHolder objects, then these objects
* define the potentially multiple properties being animated and the values the properties are
* animated between. Otherwise, the values define a single set of values animated between.
*
* @param duration The length of the animation, in milliseconds.
* @param values The set of values to animate between. If these values are not
* PropertyValuesHolder objects, then there should be more than one value, since the values
* determine the interval to animate between.
*/
public ValueAnimator(long duration, T...values) {
mDuration = duration;
if (values.length > 0) {
setValues(values);
}
}
/**
* Sets the values, per property, being animated between. This function is called internally
* by the constructors of ValueAnimator that take a list of values. But an ValueAnimator can
* be constructed without values and this method can be called to set the values manually
* instead.
*
* @param values The set of values, per property, being animated between.
*/
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
}
/**
* Returns the values that this ValueAnimator animates between. These values are stored in
* PropertyValuesHolder objects, even if the ValueAnimator was created with a simple list
* of value objects instead.
*
* @return PropertyValuesHolder[] An array of PropertyValuesHolder objects which hold the
* values, per property, that define the animation.
*/
public PropertyValuesHolder[] getValues() {
return mValues;
}
/**
* Sets the values to animate between for this animation. If <code>values</code> is
* a set of PropertyValuesHolder objects, these objects will become the set of properties
* animated and the values that those properties are animated between. Otherwise, this method
* will set only one set of values for the ValueAnimator. Also, if the values are not
* PropertyValuesHolder objects and if there are already multiple sets of
* values defined for this ValueAnimator via
* more than one PropertyValuesHolder objects, this method will set the values for
* the first of those objects.
*
* @param values The set of values to animate between.
*/
public void setValues(T... values) {
if (mValues == null || mValues.length == 0) {
setValues(new PropertyValuesHolder[]{
new PropertyValuesHolder("", (Object[])values)});
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setValues(values);
}
}
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation.
*
* <p>Overrides of this method should call the superclass method to ensure
* that internal mechanisms for the animation are set up correctly.</p>
*/
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mCurrentIteration = 0;
mInitialized = true;
}
}
/**
* Sets the length of the animation.
*
* @param duration The length of the animation, in milliseconds.
*/
public void setDuration(long duration) {
mDuration = duration;
}
/**
* Gets the length of the animation.
*
* @return The length of the animation, in milliseconds.
*/
public long getDuration() {
return mDuration;
}
/**
* Sets the position of the animation to the specified point in time. This time should
* be between 0 and the total duration of the animation, including any repetition. If
* the animation has not yet been started, then it will not advance forward after it is
* set to this time; it will simply set the time to this value and perform any appropriate
* actions based on that time. If the animation is already running, then setCurrentPlayTime()
* will set the current playing time to this value and continue playing from that point.
*
* @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
*/
public void setCurrentPlayTime(long playTime) {
initAnimation();
long currentTime = AnimationUtils.currentAnimationTimeMillis();
if (mPlayingState != RUNNING) {
mSeekTime = playTime;
mPlayingState = SEEKED;
}
mStartTime = currentTime - playTime;
animationFrame(currentTime);
}
/**
* Gets the current position of the animation in time, which is equal to the current
* time minus the time that the animation started. An animation that is not yet started will
* return a value of zero.
*
* @return The current position in time of the animation.
*/
public long getCurrentPlayTime() {
if (!mInitialized || mPlayingState == STOPPED) {
return 0;
}
return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
}
/**
* This custom, static handler handles the timing pulse that is shared by
* all active animations. This approach ensures that the setting of animation
* values will happen on the UI thread and that all animations will share
* the same times for calculating their values, which makes synchronizing
* animations possible.
*
*/
private static class AnimationHandler extends Handler {
/**
* There are only two messages that we care about: ANIMATION_START and
* ANIMATION_FRAME. The START message is sent when an animation's start()
* method is called. It cannot start synchronously when start() is called
* because the call may be on the wrong thread, and it would also not be
* synchronized with other animations because it would not start on a common
* timing pulse. So each animation sends a START message to the handler, which
* causes the handler to place the animation on the active animations queue and
* start processing frames for that animation.
* The FRAME message is the one that is sent over and over while there are any
* active animations to process.
*/
@Override
public void handleMessage(Message msg) {
boolean callAgain = true;
switch (msg.what) {
// TODO: should we avoid sending frame message when starting if we
// were already running?
case ANIMATION_START:
if (sAnimations.size() > 0 || sDelayedAnims.size() > 0) {
callAgain = false;
}
// pendingAnims holds any animations that have requested to be started
// We're going to clear sPendingAnimations, but starting animation may
// cause more to be added to the pending list (for example, if one animation
// starting triggers another starting). So we loop until sPendingAnimations
// is empty.
while (sPendingAnimations.size() > 0) {
ArrayList<ValueAnimator> pendingCopy =
(ArrayList<ValueAnimator>) sPendingAnimations.clone();
sPendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
// If the animation has a startDelay, place it on the delayed list
if (anim.mStartDelay == 0 || anim.mPlayingState == ENDED ||
anim.mPlayingState == CANCELED) {
anim.startAnimation();
} else {
sDelayedAnims.add(anim);
}
}
}
// fall through to process first frame of new animations
case ANIMATION_FRAME:
// currentTime holds the common time for all animations processed
// during this frame
long currentTime = AnimationUtils.currentAnimationTimeMillis();
// First, process animations currently sitting on the delayed queue, adding
// them to the active animations if they are ready
int numDelayedAnims = sDelayedAnims.size();
for (int i = 0; i < numDelayedAnims; ++i) {
ValueAnimator anim = sDelayedAnims.get(i);
if (anim.delayedAnimationFrame(currentTime)) {
sReadyAnims.add(anim);
}
}
int numReadyAnims = sReadyAnims.size();
if (numReadyAnims > 0) {
for (int i = 0; i < numReadyAnims; ++i) {
ValueAnimator anim = sReadyAnims.get(i);
anim.startAnimation();
sDelayedAnims.remove(anim);
}
sReadyAnims.clear();
}
// Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
int numAnims = sAnimations.size();
for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = sAnimations.get(i);
if (anim.animationFrame(currentTime)) {
sEndingAnims.add(anim);
}
}
if (sEndingAnims.size() > 0) {
for (int i = 0; i < sEndingAnims.size(); ++i) {
sEndingAnims.get(i).endAnimation();
}
sEndingAnims.clear();
}
// If there are still active or delayed animations, call the handler again
// after the frameDelay
if (callAgain && (!sAnimations.isEmpty() || !sDelayedAnims.isEmpty())) {
sendEmptyMessageDelayed(ANIMATION_FRAME, sFrameDelay);
}
break;
}
}
}
/**
* The amount of time, in milliseconds, to delay starting the animation after
* {@link #start()} is called.
*
* @return the number of milliseconds to delay running the animation
*/
public long getStartDelay() {
return mStartDelay;
}
/**
* The amount of time, in milliseconds, to delay starting the animation after
* {@link #start()} is called.
* @param startDelay The amount of the delay, in milliseconds
*/
public void setStartDelay(long startDelay) {
this.mStartDelay = startDelay;
}
/**
* The amount of time, in milliseconds, between each frame of the animation. This is a
* requested time that the animation will attempt to honor, but the actual delay between
* frames may be different, depending on system load and capabilities. This is a static
* function because the same delay will be applied to all animations, since they are all
* run off of a single timing loop.
*
* @return the requested time between frames, in milliseconds
*/
public static long getFrameDelay() {
return sFrameDelay;
}
/**
* The amount of time, in milliseconds, between each frame of the animation. This is a
* requested time that the animation will attempt to honor, but the actual delay between
* frames may be different, depending on system load and capabilities. This is a static
* function because the same delay will be applied to all animations, since they are all
* run off of a single timing loop.
*
* @param frameDelay the requested time between frames, in milliseconds
*/
public static void setFrameDelay(long frameDelay) {
sFrameDelay = frameDelay;
}
/**
* The most recent value calculated by this <code>ValueAnimator</code> when there is just one
* property being animated. This value is only sensible while the animation is running. The main
* purpose for this read-only property is to retrieve the value from the <code>ValueAnimator</code>
* during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
* is called during each animation frame, immediately after the value is calculated.
*
* @return animatedValue The value most recently calculated by this <code>ValueAnimator</code> for
* the single property being animated. If there are several properties being animated
* (specified by several PropertyValuesHolder objects in the constructor), this function
* returns the animated value for the first of those objects.
*/
public Object getAnimatedValue() {
if (mValues != null && mValues.length > 0) {
return mValues[0].getAnimatedValue();
}
// Shouldn't get here; should always have values unless ValueAnimator was set up wrong
return null;
}
/**
* The most recent value calculated by this <code>ValueAnimator</code> for <code>propertyName</code>.
* The main purpose for this read-only property is to retrieve the value from the
* <code>ValueAnimator</code> during a call to
* {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
* is called during each animation frame, immediately after the value is calculated.
*
* @return animatedValue The value most recently calculated for the named property
* by this <code>ValueAnimator</code>.
*/
public Object getAnimatedValue(String propertyName) {
PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
if (valuesHolder != null) {
return valuesHolder.getAnimatedValue();
} else {
// At least avoid crashing if called with bogus propertyName
return null;
}
}
/**
* Sets how many times the animation should be repeated. If the repeat
* count is 0, the animation is never repeated. If the repeat count is
* greater than 0 or {@link #INFINITE}, the repeat mode will be taken
* into account. The repeat count is 0 by default.
*
* @param value the number of times the animation should be repeated
*/
public void setRepeatCount(int value) {
mRepeatCount = value;
}
/**
* Defines how many times the animation should repeat. The default value
* is 0.
*
* @return the number of times the animation should repeat, or {@link #INFINITE}
*/
public int getRepeatCount() {
return mRepeatCount;
}
/**
* Defines what this animation should do when it reaches the end. This
* setting is applied only when the repeat count is either greater than
* 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
*
* @param value {@link #RESTART} or {@link #REVERSE}
*/
public void setRepeatMode(int value) {
mRepeatMode = value;
}
/**
* Defines what this animation should do when it reaches the end.
*
* @return either one of {@link #REVERSE} or {@link #RESTART}
*/
public int getRepeatMode() {
return mRepeatMode;
}
/**
* Adds a listener to the set of listeners that are sent update events through the life of
* an animation. This method is called on all listeners for every frame of the animation,
* after the values for the animation have been calculated.
*
* @param listener the listener to be added to the current set of listeners for this animation.
*/
public void addUpdateListener(AnimatorUpdateListener listener) {
if (mUpdateListeners == null) {
mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
}
mUpdateListeners.add(listener);
}
/**
* Removes a listener from the set listening to frame updates for this animation.
*
* @param listener the listener to be removed from the current set of update listeners
* for this animation.
*/
public void removeUpdateListener(AnimatorUpdateListener listener) {
if (mUpdateListeners == null) {
return;
}
mUpdateListeners.remove(listener);
if (mUpdateListeners.size() == 0) {
mUpdateListeners = null;
}
}
/**
* The time interpolator used in calculating the elapsed fraction of this animation. The
* interpolator determines whether the animation runs with linear or non-linear motion,
* such as acceleration and deceleration. The default value is
* {@link android.view.animation.AccelerateDecelerateInterpolator}
*
* @param value the interpolator to be used by this animation
*/
@Override
public void setInterpolator(Interpolator value) {
if (value != null) {
mInterpolator = value;
}
}
/**
* Returns the timing interpolator that this ValueAnimator uses.
*
* @return The timing interpolator for this ValueAnimator.
*/
public Interpolator getInterpolator() {
return mInterpolator;
}
/**
* The type evaluator to be used when calculating the animated values of this animation.
* The system will automatically assign a float, int, or double evaluator based on the type
* of <code>startValue</code> and <code>endValue</code> in the constructor. But if these values
* are not one of these primitive types, or if different evaluation is desired (such as is
* necessary with int values that represent colors), a custom evaluator needs to be assigned.
* For example, when running an animation on color values, the {@link RGBEvaluator}
* should be used to get correct RGB color interpolation.
*
* <p>If this ValueAnimator has only one set of values being animated between, this evaluator
* will be used for that set. If there are several sets of values being animated, which is
* the case if PropertyValuesHOlder objects were set on the ValueAnimator, then the evaluator
* is assigned just to the first PropertyValuesHolder object.</p>
*
* @param value the evaluator to be used this animation
*/
public void setEvaluator(TypeEvaluator value) {
if (value != null && mValues != null && mValues.length > 0) {
mValues[0].setEvaluator(value);
}
}
/**
* Start the animation playing. This version of start() takes a boolean flag that indicates
* whether the animation should play in reverse. The flag is usually false, but may be set
* to true if called from the reverse() method/
*
* @param playBackwards Whether the ValueAnimator should start playing in reverse.
*/
private void start(boolean playBackwards) {
mPlayingBackwards = playBackwards;
if ((mStartDelay == 0) && (Thread.currentThread() == Looper.getMainLooper().getThread())) {
// This sets the initial value of the animation, prior to actually starting it running
setCurrentPlayTime(getCurrentPlayTime());
}
mPlayingState = STOPPED;
mStartedDelay = false;
sPendingAnimations.add(this);
if (sAnimationHandler == null) {
sAnimationHandler = new AnimationHandler();
}
// TODO: does this put too many messages on the queue if the handler
// is already running?
sAnimationHandler.sendEmptyMessage(ANIMATION_START);
}
@Override
public void start() {
start(false);
}
@Override
public void cancel() {
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
for (AnimatorListener listener : tmpListeners) {
listener.onAnimationCancel(this);
}
}
// Just set the CANCELED flag - this causes the animation to end the next time a frame
// is processed.
mPlayingState = CANCELED;
}
@Override
public void end() {
if (!sAnimations.contains(this) && !sPendingAnimations.contains(this)) {
// Special case if the animation has not yet started; get it ready for ending
mStartedDelay = false;
sPendingAnimations.add(this);
if (sAnimationHandler == null) {
sAnimationHandler = new AnimationHandler();
}
sAnimationHandler.sendEmptyMessage(ANIMATION_START);
}
// Just set the ENDED flag - this causes the animation to end the next time a frame
// is processed.
mPlayingState = ENDED;
}
@Override
public boolean isRunning() {
// ENDED or CANCELED indicate that it has been ended or canceled, but not processed yet
return (mPlayingState == RUNNING || mPlayingState == ENDED || mPlayingState == CANCELED);
}
/**
* Plays the ValueAnimator in reverse. If the animation is already running,
* it will stop itself and play backwards from the point reached when reverse was called.
* If the animation is not currently running, then it will start from the end and
* play backwards. This behavior is only set for the current animation; future playing
* of the animation will use the default behavior of playing forward.
*/
public void reverse() {
mPlayingBackwards = !mPlayingBackwards;
if (mPlayingState == RUNNING) {
long currentTime = AnimationUtils.currentAnimationTimeMillis();
long currentPlayTime = currentTime - mStartTime;
long timeLeft = mDuration - currentPlayTime;
mStartTime = currentTime - timeLeft;
} else {
start(true);
}
}
/**
* Called internally to end an animation by removing it from the animations list. Must be
* called on the UI thread.
*/
private void endAnimation() {
sAnimations.remove(this);
mPlayingState = STOPPED;
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
for (AnimatorListener listener : tmpListeners) {
listener.onAnimationEnd(this);
}
}
}
/**
* Called internally to start an animation by adding it to the active animations list. Must be
* called on the UI thread.
*/
private void startAnimation() {
initAnimation();
sAnimations.add(this);
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
for (AnimatorListener listener : tmpListeners) {
listener.onAnimationStart(this);
}
}
}
/**
* Internal function called to process an animation frame on an animation that is currently
* sleeping through its <code>startDelay</code> phase. The return value indicates whether it
* should be woken up and put on the active animations queue.
*
* @param currentTime The current animation time, used to calculate whether the animation
* has exceeded its <code>startDelay</code> and should be started.
* @return True if the animation's <code>startDelay</code> has been exceeded and the animation
* should be added to the set of active animations.
*/
private boolean delayedAnimationFrame(long currentTime) {
if (mPlayingState == CANCELED || mPlayingState == ENDED) {
// end the delay, process an animation frame to actually cancel it
return true;
}
if (!mStartedDelay) {
mStartedDelay = true;
mDelayStartTime = currentTime;
} else {
long deltaTime = currentTime - mDelayStartTime;
if (deltaTime > mStartDelay) {
// startDelay ended - start the anim and record the
// mStartTime appropriately
mStartTime = currentTime - (deltaTime - mStartDelay);
mPlayingState = RUNNING;
return true;
}
}
return false;
}
/**
* This internal function processes a single animation frame for a given animation. The
* currentTime parameter is the timing pulse sent by the handler, used to calculate the
* elapsed duration, and therefore
* the elapsed fraction, of the animation. The return value indicates whether the animation
* should be ended (which happens when the elapsed time of the animation exceeds the
* animation's duration, including the repeatCount).
*
* @param currentTime The current time, as tracked by the static timing handler
* @return true if the animation's duration, including any repetitions due to
* <code>repeatCount</code> has been exceeded and the animation should be ended.
*/
private boolean animationFrame(long currentTime) {
boolean done = false;
if (mPlayingState == STOPPED) {
mPlayingState = RUNNING;
if (mSeekTime < 0) {
mStartTime = currentTime;
} else {
mStartTime = currentTime - mSeekTime;
// Now that we're playing, reset the seek time
mSeekTime = -1;
}
}
switch (mPlayingState) {
case RUNNING:
case SEEKED:
float fraction = (float)(currentTime - mStartTime) / mDuration;
if (fraction >= 1f) {
if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
// Time to repeat
if (mListeners != null) {
for (AnimatorListener listener : mListeners) {
listener.onAnimationRepeat(this);
}
}
++mCurrentIteration;
if (mRepeatMode == REVERSE) {
mPlayingBackwards = mPlayingBackwards ? false : true;
}
// TODO: doesn't account for fraction going Wayyyyy over 1, like 2+
fraction = fraction - 1f;
mStartTime += mDuration;
} else {
done = true;
fraction = Math.min(fraction, 1.0f);
}
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
animateValue(fraction);
break;
case ENDED:
// The final value set on the target varies, depending on whether the animation
// was supposed to repeat an odd number of times
if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
animateValue(0f);
} else {
animateValue(1f);
}
// Fall through to set done flag
case CANCELED:
done = true;
mPlayingState = STOPPED;
break;
}
return done;
}
/**
* This method is called with the elapsed fraction of the animation during every
* animation frame. This function turns the elapsed fraction into an interpolated fraction
* and then into an animated value (from the evaluator. The function is called mostly during
* animation updates, but it is also called when the <code>end()</code>
* function is called, to set the final value on the property.
*
* <p>Overrides of this method must call the superclass to perform the calculation
* of the animated value.</p>
*
* @param fraction The elapsed fraction of the animation.
*/
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
@Override
public ValueAnimator clone() {
final ValueAnimator anim = (ValueAnimator) super.clone();
if (mUpdateListeners != null) {
ArrayList<AnimatorUpdateListener> oldListeners = mUpdateListeners;
anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
int numListeners = oldListeners.size();
for (int i = 0; i < numListeners; ++i) {
anim.mUpdateListeners.add(oldListeners.get(i));
}
}
anim.mSeekTime = -1;
anim.mPlayingBackwards = false;
anim.mCurrentIteration = 0;
anim.mInitialized = false;
anim.mPlayingState = STOPPED;
anim.mStartedDelay = false;
PropertyValuesHolder[] oldValues = mValues;
if (oldValues != null) {
int numValues = oldValues.length;
anim.mValues = new PropertyValuesHolder[numValues];
for (int i = 0; i < numValues; ++i) {
anim.mValues[i] = oldValues[i].clone();
}
anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = mValues[i];
anim.mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
}
return anim;
}
/**
* Implementors of this interface can add themselves as update listeners
* to an <code>ValueAnimator</code> instance to receive callbacks on every animation
* frame, after the current frame's values have been calculated for that
* <code>ValueAnimator</code>.
*/
public static interface AnimatorUpdateListener {
/**
* <p>Notifies the occurrence of another frame of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationUpdate(ValueAnimator animation);
}
}

View File

@@ -16,7 +16,7 @@
package android.app;
import android.animation.Animatable;
import android.animation.Animator;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.Intent;
@@ -26,7 +26,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.AndroidRuntimeException;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -755,7 +754,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
/**
* Called when a fragment loads an animation.
*/
public Animatable onCreateAnimatable(int transit, boolean enter, int nextAnim) {
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
return null;
}

View File

@@ -16,10 +16,10 @@
package android.app;
import android.animation.Animatable;
import android.animation.AnimatableInflater;
import android.animation.PropertyAnimator;
import android.animation.Sequencer;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
@@ -248,16 +248,16 @@ final class FragmentManagerImpl implements FragmentManager {
return f;
}
Animatable loadAnimatable(Fragment fragment, int transit, boolean enter,
Animator loadAnimator(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
Animatable animObj = fragment.onCreateAnimatable(transit, enter,
Animator animObj = fragment.onCreateAnimator(transit, enter,
fragment.mNextAnim);
if (animObj != null) {
return animObj;
}
if (fragment.mNextAnim != 0) {
Animatable anim = AnimatableInflater.loadAnimatable(mActivity, fragment.mNextAnim);
Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim);
if (anim != null) {
return anim;
}
@@ -288,7 +288,7 @@ final class FragmentManagerImpl implements FragmentManager {
return null;
}
return AnimatableInflater.loadAnimatable(mActivity, anim);
return AnimatorInflater.loadAnimator(mActivity, anim);
}
void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
@@ -360,13 +360,13 @@ final class FragmentManagerImpl implements FragmentManager {
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
Animatable anim = loadAnimatable(f, transit, true,
Animator anim = loadAnimator(f, transit, true,
transitionStyle);
if (anim != null) {
if (anim instanceof Sequencer) {
((Sequencer)anim).setTarget(f.mView);
} else if (anim instanceof PropertyAnimator) {
((PropertyAnimator)anim).setTarget(f.mView);
if (anim instanceof AnimatorSet) {
((AnimatorSet)anim).setTarget(f.mView);
} else if (anim instanceof ObjectAnimator) {
((ObjectAnimator)anim).setTarget(f.mView);
}
anim.start();
}
@@ -448,13 +448,13 @@ final class FragmentManagerImpl implements FragmentManager {
}
if (f.mView != null && f.mContainer != null) {
if (mCurState > Fragment.INITIALIZING) {
Animatable anim = loadAnimatable(f, transit, true,
Animator anim = loadAnimator(f, transit, true,
transitionStyle);
if (anim != null) {
if (anim instanceof Sequencer) {
((Sequencer)anim).setTarget(f.mView);
} else if (anim instanceof PropertyAnimator) {
((PropertyAnimator)anim).setTarget(f.mView);
if (anim instanceof AnimatorSet) {
((AnimatorSet)anim).setTarget(f.mView);
} else if (anim instanceof ObjectAnimator) {
((ObjectAnimator)anim).setTarget(f.mView);
}
anim.start();
}
@@ -588,13 +588,13 @@ final class FragmentManagerImpl implements FragmentManager {
if (!fragment.mHidden) {
fragment.mHidden = true;
if (fragment.mView != null) {
Animatable anim = loadAnimatable(fragment, transition, true,
Animator anim = loadAnimator(fragment, transition, true,
transitionStyle);
if (anim != null) {
if (anim instanceof Sequencer) {
((Sequencer)anim).setTarget(fragment.mView);
} else if (anim instanceof PropertyAnimator) {
((PropertyAnimator)anim).setTarget(fragment.mView);
if (anim instanceof AnimatorSet) {
((AnimatorSet)anim).setTarget(fragment.mView);
} else if (anim instanceof ObjectAnimator) {
((ObjectAnimator)anim).setTarget(fragment.mView);
}
anim.start();
}
@@ -612,13 +612,13 @@ final class FragmentManagerImpl implements FragmentManager {
if (fragment.mHidden) {
fragment.mHidden = false;
if (fragment.mView != null) {
Animatable anim = loadAnimatable(fragment, transition, true,
Animator anim = loadAnimator(fragment, transition, true,
transitionStyle);
if (anim != null) {
if (anim instanceof Sequencer) {
((Sequencer)anim).setTarget(fragment.mView);
} else if (anim instanceof PropertyAnimator) {
((PropertyAnimator)anim).setTarget(fragment.mView);
if (anim instanceof AnimatorSet) {
((AnimatorSet)anim).setTarget(fragment.mView);
} else if (anim instanceof ObjectAnimator) {
((ObjectAnimator)anim).setTarget(fragment.mView);
}
anim.start();
}

View File

@@ -35,7 +35,7 @@ import java.io.IOException;
public class AnimationUtils {
/**
* These flags are used when parsing Sequencer objects
* These flags are used when parsing AnimatorSet objects
*/
private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;

View File

@@ -18,12 +18,10 @@ package android.widget;
import java.util.ArrayList;
import android.animation.PropertyAnimator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
@@ -220,13 +218,13 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
* @param view The view that is being animated
*/
void animateViewForTransition(int fromIndex, int toIndex, View view) {
PropertyAnimator pa;
ObjectAnimator pa;
if (fromIndex == -1) {
view.setAlpha(0.0f);
pa = new PropertyAnimator(400, view, "alpha", 0.0f, 1.0f);
pa = new ObjectAnimator(400, view, "alpha", 0.0f, 1.0f);
pa.start();
} else if (toIndex == -1) {
pa = new PropertyAnimator(400, view, "alpha", 1.0f, 0.0f);
pa = new ObjectAnimator(400, view, "alpha", 1.0f, 0.0f);
pa.start();
}
}

View File

@@ -16,8 +16,8 @@
package android.widget;
import android.animation.PropertyAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
@@ -166,7 +166,7 @@ public class StackView extends AdapterViewAnimator {
}
view.setVisibility(VISIBLE);
PropertyAnimator<Float> fadeIn = new PropertyAnimator<Float>(DEFAULT_ANIMATION_DURATION,
ObjectAnimator<Float> fadeIn = new ObjectAnimator<Float>(DEFAULT_ANIMATION_DURATION,
view, "alpha", view.getAlpha(), 1.0f);
fadeIn.start();
} else if (fromIndex == mNumActiveViews - 1 && toIndex == mNumActiveViews - 2) {
@@ -181,7 +181,7 @@ public class StackView extends AdapterViewAnimator {
new PropertyValuesHolder<Float>("YProgress", 0.0f);
PropertyValuesHolder<Float> slideInX =
new PropertyValuesHolder<Float>("XProgress", 0.0f);
PropertyAnimator pa = new PropertyAnimator(duration, animationSlider,
ObjectAnimator pa = new ObjectAnimator(duration, animationSlider,
slideInX, slideInY);
pa.setInterpolator(new LinearInterpolator());
pa.start();
@@ -196,7 +196,7 @@ public class StackView extends AdapterViewAnimator {
new PropertyValuesHolder<Float>("YProgress", 1.0f);
PropertyValuesHolder<Float> slideOutX =
new PropertyValuesHolder<Float>("XProgress", 0.0f);
PropertyAnimator pa = new PropertyAnimator(duration, animationSlider,
ObjectAnimator pa = new ObjectAnimator(duration, animationSlider,
slideOutX, slideOutY);
pa.setInterpolator(new LinearInterpolator());
pa.start();
@@ -208,7 +208,7 @@ public class StackView extends AdapterViewAnimator {
lp.setVerticalOffset(-mViewHeight);
} else if (toIndex == -1) {
// Fade item out
PropertyAnimator<Float> fadeOut = new PropertyAnimator<Float>
ObjectAnimator<Float> fadeOut = new ObjectAnimator<Float>
(DEFAULT_ANIMATION_DURATION, view, "alpha", view.getAlpha(), 0.0f);
fadeOut.start();
}
@@ -234,7 +234,7 @@ public class StackView extends AdapterViewAnimator {
PropertyValuesHolder<Float> translationY =
new PropertyValuesHolder<Float>("translationY", transY);
PropertyAnimator pa = new PropertyAnimator(100, view, scaleX, scaleY, translationY);
ObjectAnimator pa = new ObjectAnimator(100, view, scaleX, scaleY, translationY);
pa.start();
}
}
@@ -510,7 +510,7 @@ public class StackView extends AdapterViewAnimator {
new PropertyValuesHolder<Float>("YProgress", finalYProgress);
PropertyValuesHolder<Float> snapBackX =
new PropertyValuesHolder<Float>("XProgress", 0.0f);
PropertyAnimator pa = new PropertyAnimator(duration, animationSlider,
ObjectAnimator pa = new ObjectAnimator(duration, animationSlider,
snapBackX, snapBackY);
pa.setInterpolator(new LinearInterpolator());
pa.start();
@@ -529,7 +529,7 @@ public class StackView extends AdapterViewAnimator {
new PropertyValuesHolder<Float>("YProgress", finalYProgress);
PropertyValuesHolder<Float> snapBackX =
new PropertyValuesHolder<Float>("XProgress", 0.0f);
PropertyAnimator pa = new PropertyAnimator(duration, animationSlider,
ObjectAnimator pa = new ObjectAnimator(duration, animationSlider,
snapBackX, snapBackY);
pa.start();
}
@@ -870,7 +870,7 @@ public class StackView extends AdapterViewAnimator {
private Rect invalidateRect = new Rect();
private RectF invalidateRectf = new RectF();
// This is public so that PropertyAnimator can access it
// This is public so that ObjectAnimator can access it
public void setVerticalOffset(int newVerticalOffset) {
int offsetDelta = newVerticalOffset - verticalOffset;
verticalOffset = newVerticalOffset;

View File

@@ -416,7 +416,7 @@ public class SlidingTab extends ViewGroup {
}
/**
* Start animating the slider. Note we need two animations since an Animator
* Start animating the slider. Note we need two animations since an ValueAnimator
* keeps internal state of the invalidation region which is just the view being animated.
*
* @param anim1

View File

@@ -17,7 +17,7 @@
*/
-->
<property xmlns:android="http://schemas.android.com/apk/res/android"
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@anim/accelerate_interpolator"
android:valueFrom="0"
android:valueTo="1"

View File

@@ -17,7 +17,7 @@
*/
-->
<property xmlns:android="http://schemas.android.com/apk/res/android"
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@anim/accelerate_interpolator"
android:valueFrom="1.0"
android:valueTo="0.0"

View File

@@ -16,33 +16,33 @@
** limitations under the License.
*/
-->
<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
<property
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="@anim/decelerate_interpolator"
android:valueFrom="2"
android:valueTo="1"
android:valueType="floatType"
android:propertyName="scaleX"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:interpolator="@anim/decelerate_interpolator"
android:valueFrom="2"
android:valueTo="1"
android:valueType="floatType"
android:propertyName="scaleY"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:interpolator="@anim/decelerate_interpolator"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:propertyName="alpha"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:interpolator="@anim/decelerate_interpolator"
android:valueFrom="-400"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="translationX"
android:duration="@android:integer/config_mediumAnimTime"/>
</sequencer>
</set>

View File

@@ -16,33 +16,33 @@
** limitations under the License.
*/
-->
<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
<property
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="@anim/accelerate_interpolator"
android:valueFrom="1"
android:valueTo=".5"
android:valueType="floatType"
android:propertyName="scaleX"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:interpolator="@anim/accelerate_interpolator"
android:valueFrom="1"
android:valueTo=".5"
android:valueType="floatType"
android:propertyName="scaleY"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:interpolator="@anim/accelerate_interpolator"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="alpha"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:interpolator="@anim/accelerate_interpolator"
android:valueFrom="0"
android:valueTo="400"
android:valueType="floatType"
android:propertyName="translationX"
android:duration="@android:integer/config_mediumAnimTime"/>
</sequencer>
</set>

View File

@@ -16,31 +16,31 @@
** limitations under the License.
*/
-->
<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
<property
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="@anim/decelerate_interpolator"
android:valueFrom="2"
android:valueTo="1"
android:valueType="floatType"
android:propertyName="scaleX"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:interpolator="@anim/decelerate_interpolator"
android:valueFrom="2"
android:valueTo="1"
android:valueType="floatType"
android:propertyName="scaleY"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:propertyName="alpha"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:valueFrom="400"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="translationX"
android:duration="@android:integer/config_mediumAnimTime"/>
</sequencer>
</set>

View File

@@ -16,31 +16,31 @@
** limitations under the License.
*/
-->
<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
<property
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="@anim/accelerate_interpolator"
android:valueFrom="1"
android:valueTo="2"
android:valueType="floatType"
android:propertyName="scaleX"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:interpolator="@anim/accelerate_interpolator"
android:valueFrom="1"
android:valueTo="2"
android:valueType="floatType"
android:propertyName="scaleY"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="alpha"
android:duration="@android:integer/config_mediumAnimTime"/>
<property
<objectAnimator
android:valueFrom="0"
android:valueTo="-400"
android:valueType="floatType"
android:propertyName="translationX"
android:duration="@android:integer/config_mediumAnimTime"/>
</sequencer>
</set>

View File

@@ -3010,7 +3010,7 @@
</declare-styleable>
<!-- ========================== -->
<!-- Animator class attributes -->
<!-- ValueAnimator class attributes -->
<!-- ========================== -->
<eat-comment />
@@ -3047,7 +3047,7 @@
</declare-styleable>
<!-- ========================== -->
<!-- PropertyAnimator class attributes -->
<!-- ObjectAnimator class attributes -->
<!-- ========================== -->
<eat-comment />
@@ -3058,11 +3058,11 @@
<!-- ========================== -->
<!-- Sequencer class attributes -->
<!-- AnimatorSet class attributes -->
<!-- ========================== -->
<eat-comment />
<declare-styleable name="Sequencer">
<declare-styleable name="AnimatorSet">
<!-- Name of the property being animated. -->
<attr name="ordering">
<!-- child animations should be played together. -->

View File

@@ -16,13 +16,9 @@
package com.android.systemui.statusbar.tablet;
import android.animation.Animator;
import android.app.ActivityManagerNative;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.app.StatusBarManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -33,7 +29,6 @@ import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Slog;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -41,7 +36,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RemoteViews;
@@ -50,7 +44,6 @@ import android.widget.TextSwitcher;
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.statusbar.*;