Loading api/current.txt +16 −11 Original line number Diff line number Diff line Loading @@ -3052,9 +3052,10 @@ package android.animation { method public android.graphics.Rect evaluate(float, android.graphics.Rect, android.graphics.Rect); } public class StateListAnimator { public class StateListAnimator implements java.lang.Cloneable { ctor public StateListAnimator(); method public void addState(int[], android.animation.Animator); method public android.animation.StateListAnimator clone(); method public void jumpToCurrentState(); } Loading Loading @@ -35428,13 +35429,13 @@ package android.view.accessibility { package android.view.animation { public class AccelerateDecelerateInterpolator implements android.view.animation.Interpolator { public class AccelerateDecelerateInterpolator extends android.view.animation.BaseInterpolator { ctor public AccelerateDecelerateInterpolator(); ctor public AccelerateDecelerateInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class AccelerateInterpolator implements android.view.animation.Interpolator { public class AccelerateInterpolator extends android.view.animation.BaseInterpolator { ctor public AccelerateInterpolator(); ctor public AccelerateInterpolator(float); ctor public AccelerateInterpolator(android.content.Context, android.util.AttributeSet); Loading Loading @@ -35536,14 +35537,14 @@ package android.view.animation { method public static android.view.animation.Animation makeOutAnimation(android.content.Context, boolean); } public class AnticipateInterpolator implements android.view.animation.Interpolator { public class AnticipateInterpolator extends android.view.animation.BaseInterpolator { ctor public AnticipateInterpolator(); ctor public AnticipateInterpolator(float); ctor public AnticipateInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class AnticipateOvershootInterpolator implements android.view.animation.Interpolator { public class AnticipateOvershootInterpolator extends android.view.animation.BaseInterpolator { ctor public AnticipateOvershootInterpolator(); ctor public AnticipateOvershootInterpolator(float); ctor public AnticipateOvershootInterpolator(float, float); Loading @@ -35551,19 +35552,23 @@ package android.view.animation { method public float getInterpolation(float); } public class BounceInterpolator implements android.view.animation.Interpolator { public abstract class BaseInterpolator implements android.view.animation.Interpolator { ctor public BaseInterpolator(); } public class BounceInterpolator extends android.view.animation.BaseInterpolator { ctor public BounceInterpolator(); ctor public BounceInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class CycleInterpolator implements android.view.animation.Interpolator { public class CycleInterpolator extends android.view.animation.BaseInterpolator { ctor public CycleInterpolator(float); ctor public CycleInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class DecelerateInterpolator implements android.view.animation.Interpolator { public class DecelerateInterpolator extends android.view.animation.BaseInterpolator { ctor public DecelerateInterpolator(); ctor public DecelerateInterpolator(float); ctor public DecelerateInterpolator(android.content.Context, android.util.AttributeSet); Loading Loading @@ -35638,20 +35643,20 @@ package android.view.animation { field public int index; } public class LinearInterpolator implements android.view.animation.Interpolator { public class LinearInterpolator extends android.view.animation.BaseInterpolator { ctor public LinearInterpolator(); ctor public LinearInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class OvershootInterpolator implements android.view.animation.Interpolator { public class OvershootInterpolator extends android.view.animation.BaseInterpolator { ctor public OvershootInterpolator(); ctor public OvershootInterpolator(float); ctor public OvershootInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class PathInterpolator implements android.view.animation.Interpolator { public class PathInterpolator extends android.view.animation.BaseInterpolator { ctor public PathInterpolator(android.graphics.Path); ctor public PathInterpolator(float, float); ctor public PathInterpolator(float, float, float, float); core/java/android/animation/Animator.java +103 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.animation; import android.content.res.ConstantState; import java.util.ArrayList; /** Loading @@ -40,6 +42,18 @@ public abstract class Animator implements Cloneable { */ boolean mPaused = false; /** * A set of flags which identify the type of configuration changes that can affect this * Animator. Used by the Animator cache. */ int mChangingConfigurations = 0; /** * If this animator is inflated from a constant state, keep a reference to it so that * ConstantState will not be garbage collected until this animator is collected */ private AnimatorConstantState mConstantState; /** * Starts this animation. If the animation has a nonzero startDelay, the animation will start * running after that delay elapses. A non-delayed animation will have its initial Loading Loading @@ -295,25 +309,71 @@ public abstract class Animator implements Cloneable { } } /** * Return a mask of the configuration parameters for which this animator may change, requiring * that it should be re-created from Resources. The default implementation returns whatever * value was provided through setChangingConfigurations(int) or 0 by default. * * @return Returns a mask of the changing configuration parameters, as defined by * {@link android.content.pm.ActivityInfo}. * @see android.content.pm.ActivityInfo * @hide */ public int getChangingConfigurations() { return mChangingConfigurations; } /** * Set a mask of the configuration parameters for which this animator may change, requiring * that it be re-created from resource. * * @param configs A mask of the changing configuration parameters, as * defined by {@link android.content.pm.ActivityInfo}. * * @see android.content.pm.ActivityInfo * @hide */ public void setChangingConfigurations(int configs) { mChangingConfigurations = configs; } /** * Sets the changing configurations value to the union of the current changing configurations * and the provided configs. * This method is called while loading the animator. * @hide */ public void appendChangingConfigurations(int configs) { mChangingConfigurations |= configs; } /** * Return a {@link android.content.res.ConstantState} instance that holds the shared state of * this Animator. * <p> * This constant state is used to create new instances of this animator when needed, instead * of re-loading it from resources. Default implementation creates a new * {@link AnimatorConstantState}. You can override this method to provide your custom logic or * return null if you don't want this animator to be cached. * * @return The ConfigurationBoundResourceCache.BaseConstantState associated to this Animator. * @see android.content.res.ConstantState * @see #clone() * @hide */ public ConstantState<Animator> createConstantState() { return new AnimatorConstantState(this); } @Override public Animator clone() { try { final Animator anim = (Animator) super.clone(); if (mListeners != null) { ArrayList<AnimatorListener> oldListeners = mListeners; anim.mListeners = new ArrayList<AnimatorListener>(); int numListeners = oldListeners.size(); for (int i = 0; i < numListeners; ++i) { anim.mListeners.add(oldListeners.get(i)); } anim.mListeners = new ArrayList<AnimatorListener>(mListeners); } if (mPauseListeners != null) { ArrayList<AnimatorPauseListener> oldListeners = mPauseListeners; anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(); int numListeners = oldListeners.size(); for (int i = 0; i < numListeners; ++i) { anim.mPauseListeners.add(oldListeners.get(i)); } anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners); } return anim; } catch (CloneNotSupportedException e) { Loading Loading @@ -469,4 +529,35 @@ public abstract class Animator implements Cloneable { public void setAllowRunningAsynchronously(boolean mayRunAsync) { // It is up to subclasses to support this, if they can. } /** * Creates a {@link ConstantState} which holds changing configurations information associated * with the given Animator. * <p> * When {@link #newInstance()} is called, default implementation clones the Animator. */ private static class AnimatorConstantState extends ConstantState<Animator> { final Animator mAnimator; int mChangingConf; public AnimatorConstantState(Animator animator) { mAnimator = animator; // ensure a reference back to here so that constante state is not gc'ed. mAnimator.mConstantState = this; mChangingConf = mAnimator.getChangingConfigurations(); } @Override public int getChangingConfigurations() { return mChangingConf; } @Override public Animator newInstance() { final Animator clone = mAnimator.clone(); clone.mConstantState = this; return clone; } } } core/java/android/animation/AnimatorInflater.java +73 −17 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.animation; import android.content.Context; import android.content.res.ConfigurationBoundResourceCache; import android.content.res.ConstantState; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; Loading @@ -30,6 +32,8 @@ import android.util.TypedValue; import android.util.Xml; import android.view.InflateException; import android.view.animation.AnimationUtils; import android.view.animation.BaseInterpolator; import android.view.animation.Interpolator; import com.android.internal.R; Loading Loading @@ -67,6 +71,9 @@ public class AnimatorInflater { private static final boolean DBG_ANIMATOR_INFLATER = false; // used to calculate changing configs for resource references private static final TypedValue sTmpTypedValue = new TypedValue(); /** * Loads an {@link Animator} object from a resource * Loading Loading @@ -98,11 +105,34 @@ public class AnimatorInflater { /** @hide */ public static Animator loadAnimator(Resources resources, Theme theme, int id, float pathErrorScale) throws NotFoundException { final ConfigurationBoundResourceCache<Animator> animatorCache = resources .getAnimatorCache(); Animator animator = animatorCache.get(id, theme); if (animator != null) { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id)); } return animator; } else if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "cache miss for animator " + resources.getResourceName(id)); } XmlResourceParser parser = null; try { parser = resources.getAnimation(id); return createAnimatorFromXml(resources, theme, parser, pathErrorScale); animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<Animator> constantState = animator.createConstantState(); if (constantState != null) { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "caching animator for res " + resources.getResourceName(id)); } animatorCache.put(id, theme, constantState); // create a new animator so that cached version is never used by the user animator = constantState.newInstance(resources, theme); } } return animator; } catch (XmlPullParserException ex) { Resources.NotFoundException rnf = new Resources.NotFoundException("Can't load animation resource ID #0x" + Loading @@ -122,10 +152,29 @@ public class AnimatorInflater { public static StateListAnimator loadStateListAnimator(Context context, int id) throws NotFoundException { final Resources resources = context.getResources(); final ConfigurationBoundResourceCache<StateListAnimator> cache = resources .getStateListAnimatorCache(); final Theme theme = context.getTheme(); StateListAnimator animator = cache.get(id, theme); if (animator != null) { return animator; } XmlResourceParser parser = null; try { parser = context.getResources().getAnimation(id); return createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); parser = resources.getAnimation(id); animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<StateListAnimator> constantState = animator .createConstantState(); if (constantState != null) { cache.put(id, theme, constantState); // return a clone so that the animator in constant state is never used. animator = constantState.newInstance(resources, theme); } } return animator; } catch (XmlPullParserException ex) { Resources.NotFoundException rnf = new Resources.NotFoundException( Loading Loading @@ -172,14 +221,13 @@ public class AnimatorInflater { for (int i = 0; i < attributeCount; i++) { int attrName = attributeSet.getAttributeNameResource(i); if (attrName == R.attr.animation) { animator = loadAnimator(context, attributeSet.getAttributeResourceValue(i, 0)); final int animId = attributeSet.getAttributeResourceValue(i, 0); animator = loadAnimator(context, animId); } else { states[stateIndex++] = attributeSet.getAttributeBooleanValue(i, false) ? attrName : -attrName; } } if (animator == null) { animator = createAnimatorFromXml(context.getResources(), Loading @@ -192,7 +240,6 @@ public class AnimatorInflater { } stateListAnimator .addState(StateSet.trimStateSet(states, stateIndex), animator); } break; } Loading Loading @@ -508,7 +555,6 @@ public class AnimatorInflater { private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser, AttributeSet attrs, AnimatorSet parent, int sequenceOrdering, float pixelSize) throws XmlPullParserException, IOException { Animator anim = null; ArrayList<Animator> childAnims = null; Loading Loading @@ -537,8 +583,8 @@ public class AnimatorInflater { } else { a = res.obtainAttributes(attrs, R.styleable.AnimatorSet); } int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER); anim.appendChangingConfigurations(a.getChangingConfigurations()); int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER); createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering, pixelSize); a.recycle(); Loading @@ -565,7 +611,6 @@ public class AnimatorInflater { parent.playSequentially(animsArray); } } return anim; } Loading @@ -591,7 +636,6 @@ public class AnimatorInflater { private static ValueAnimator loadAnimator(Resources res, Theme theme, AttributeSet attrs, ValueAnimator anim, float pathErrorScale) throws NotFoundException { TypedArray arrayAnimator = null; TypedArray arrayObjectAnimator = null; Loading @@ -609,25 +653,37 @@ public class AnimatorInflater { } else { arrayObjectAnimator = res.obtainAttributes(attrs, R.styleable.PropertyAnimator); } anim.appendChangingConfigurations(arrayObjectAnimator.getChangingConfigurations()); } if (anim == null) { anim = new ValueAnimator(); } anim.appendChangingConfigurations(arrayAnimator.getChangingConfigurations()); parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator, pathErrorScale); final int resID = arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0); final int resID = arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0); if (resID > 0) { anim.setInterpolator(AnimationUtils.loadInterpolator(res, theme, resID)); final Interpolator interpolator = AnimationUtils.loadInterpolator(res, theme, resID); if (interpolator instanceof BaseInterpolator) { anim.appendChangingConfigurations( ((BaseInterpolator) interpolator).getChangingConfiguration()); } anim.setInterpolator(interpolator); } arrayAnimator.recycle(); if (arrayObjectAnimator != null) { arrayObjectAnimator.recycle(); } return anim; } private static int getChangingConfigs(Resources resources, int id) { synchronized (sTmpTypedValue) { resources.getValue(id, sTmpTypedValue, true); return sTmpTypedValue.changingConfigurations; } } } core/java/android/animation/AnimatorSet.java +54 −24 Original line number Diff line number Diff line Loading @@ -240,6 +240,19 @@ public final class AnimatorSet extends Animator { } } /** * @hide */ @Override public int getChangingConfigurations() { int conf = super.getChangingConfigurations(); final int nodeCount = mNodes.size(); for (int i = 0; i < nodeCount; i ++) { conf |= mNodes.get(i).animation.getChangingConfigurations(); } return conf; } /** * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations} * of this AnimatorSet. The default value is null, which means that no interpolator Loading Loading @@ -628,23 +641,25 @@ public final class AnimatorSet extends Animator { * manually, as we clone each Node (and its animation). The clone will then be sorted, * and will populate any appropriate lists, when it is started. */ final int nodeCount = mNodes.size(); anim.mNeedsSort = true; anim.mTerminated = false; anim.mStarted = false; anim.mPlayingSet = new ArrayList<Animator>(); anim.mNodeMap = new HashMap<Animator, Node>(); anim.mNodes = new ArrayList<Node>(); anim.mSortedNodes = new ArrayList<Node>(); anim.mNodes = new ArrayList<Node>(nodeCount); anim.mSortedNodes = new ArrayList<Node>(nodeCount); anim.mReversible = mReversible; anim.mSetListener = null; // 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 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) { for (int n = 0; n < nodeCount; n++) { final Node node = mNodes.get(n); Node nodeClone = node.clone(); nodeCloneMap.put(node, nodeClone); node.mTmpClone = nodeClone; anim.mNodes.add(nodeClone); anim.mNodeMap.put(nodeClone.animation, nodeClone); // Clear out the dependencies in the clone; we'll set these up manually later Loading @@ -652,40 +667,50 @@ public final class AnimatorSet extends Animator { nodeClone.tmpDependencies = null; nodeClone.nodeDependents = null; nodeClone.nodeDependencies = null; // clear out any listeners that were set up by the AnimatorSet; these will // be set up when the clone's nodes are sorted ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); final ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); if (cloneListeners != null) { ArrayList<AnimatorListener> listenersToRemove = null; for (AnimatorListener listener : cloneListeners) { for (int i = cloneListeners.size() - 1; i >= 0; i--) { final AnimatorListener listener = cloneListeners.get(i); if (listener instanceof AnimatorSetListener) { if (listenersToRemove == null) { listenersToRemove = new ArrayList<AnimatorListener>(); } listenersToRemove.add(listener); } } if (listenersToRemove != null) { for (AnimatorListener listener : listenersToRemove) { cloneListeners.remove(listener); cloneListeners.remove(i); } } } } // Now that we've cloned all of the nodes, we're ready to walk through their // dependencies, mapping the old dependencies to the new nodes for (Node node : mNodes) { Node nodeClone = nodeCloneMap.get(node); for (int n = 0; n < nodeCount; n++) { final Node node = mNodes.get(n); final Node clone = node.mTmpClone; if (node.dependencies != null) { for (Dependency dependency : node.dependencies) { Node clonedDependencyNode = nodeCloneMap.get(dependency.node); Dependency cloneDependency = new Dependency(clonedDependencyNode, clone.dependencies = new ArrayList<Dependency>(node.dependencies.size()); final int depSize = node.dependencies.size(); for (int i = 0; i < depSize; i ++) { final Dependency dependency = node.dependencies.get(i); Dependency cloneDependency = new Dependency(dependency.node.mTmpClone, dependency.rule); nodeClone.addDependency(cloneDependency); clone.dependencies.add(cloneDependency); } } if (node.nodeDependents != null) { clone.nodeDependents = new ArrayList<Node>(node.nodeDependents.size()); for (Node dep : node.nodeDependents) { clone.nodeDependents.add(dep.mTmpClone); } } if (node.nodeDependencies != null) { clone.nodeDependencies = new ArrayList<Node>(node.nodeDependencies.size()); for (Node dep : node.nodeDependencies) { clone.nodeDependencies.add(dep.mTmpClone); } } } for (int n = 0; n < nodeCount; n++) { mNodes.get(n).mTmpClone = null; } return anim; } Loading Loading @@ -1016,6 +1041,11 @@ public final class AnimatorSet extends Animator { */ public boolean done = false; /** * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete */ private Node mTmpClone = null; /** * Constructs the Node with the animation that it encapsulates. A Node has no * dependencies by default; dependencies are added via the addDependency() Loading core/java/android/animation/FloatKeyframeSet.java +3 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.animation; import android.animation.Keyframe.FloatKeyframe; import java.util.ArrayList; import java.util.List; /** * This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate Loading Loading @@ -47,8 +48,8 @@ class FloatKeyframeSet extends KeyframeSet implements Keyframes.FloatKeyframes { @Override public FloatKeyframeSet clone() { ArrayList<Keyframe> keyframes = mKeyframes; int numKeyframes = mKeyframes.size(); final List<Keyframe> keyframes = mKeyframes; final int numKeyframes = mKeyframes.size(); FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes]; for (int i = 0; i < numKeyframes; ++i) { newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone(); Loading Loading
api/current.txt +16 −11 Original line number Diff line number Diff line Loading @@ -3052,9 +3052,10 @@ package android.animation { method public android.graphics.Rect evaluate(float, android.graphics.Rect, android.graphics.Rect); } public class StateListAnimator { public class StateListAnimator implements java.lang.Cloneable { ctor public StateListAnimator(); method public void addState(int[], android.animation.Animator); method public android.animation.StateListAnimator clone(); method public void jumpToCurrentState(); } Loading Loading @@ -35428,13 +35429,13 @@ package android.view.accessibility { package android.view.animation { public class AccelerateDecelerateInterpolator implements android.view.animation.Interpolator { public class AccelerateDecelerateInterpolator extends android.view.animation.BaseInterpolator { ctor public AccelerateDecelerateInterpolator(); ctor public AccelerateDecelerateInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class AccelerateInterpolator implements android.view.animation.Interpolator { public class AccelerateInterpolator extends android.view.animation.BaseInterpolator { ctor public AccelerateInterpolator(); ctor public AccelerateInterpolator(float); ctor public AccelerateInterpolator(android.content.Context, android.util.AttributeSet); Loading Loading @@ -35536,14 +35537,14 @@ package android.view.animation { method public static android.view.animation.Animation makeOutAnimation(android.content.Context, boolean); } public class AnticipateInterpolator implements android.view.animation.Interpolator { public class AnticipateInterpolator extends android.view.animation.BaseInterpolator { ctor public AnticipateInterpolator(); ctor public AnticipateInterpolator(float); ctor public AnticipateInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class AnticipateOvershootInterpolator implements android.view.animation.Interpolator { public class AnticipateOvershootInterpolator extends android.view.animation.BaseInterpolator { ctor public AnticipateOvershootInterpolator(); ctor public AnticipateOvershootInterpolator(float); ctor public AnticipateOvershootInterpolator(float, float); Loading @@ -35551,19 +35552,23 @@ package android.view.animation { method public float getInterpolation(float); } public class BounceInterpolator implements android.view.animation.Interpolator { public abstract class BaseInterpolator implements android.view.animation.Interpolator { ctor public BaseInterpolator(); } public class BounceInterpolator extends android.view.animation.BaseInterpolator { ctor public BounceInterpolator(); ctor public BounceInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class CycleInterpolator implements android.view.animation.Interpolator { public class CycleInterpolator extends android.view.animation.BaseInterpolator { ctor public CycleInterpolator(float); ctor public CycleInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class DecelerateInterpolator implements android.view.animation.Interpolator { public class DecelerateInterpolator extends android.view.animation.BaseInterpolator { ctor public DecelerateInterpolator(); ctor public DecelerateInterpolator(float); ctor public DecelerateInterpolator(android.content.Context, android.util.AttributeSet); Loading Loading @@ -35638,20 +35643,20 @@ package android.view.animation { field public int index; } public class LinearInterpolator implements android.view.animation.Interpolator { public class LinearInterpolator extends android.view.animation.BaseInterpolator { ctor public LinearInterpolator(); ctor public LinearInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class OvershootInterpolator implements android.view.animation.Interpolator { public class OvershootInterpolator extends android.view.animation.BaseInterpolator { ctor public OvershootInterpolator(); ctor public OvershootInterpolator(float); ctor public OvershootInterpolator(android.content.Context, android.util.AttributeSet); method public float getInterpolation(float); } public class PathInterpolator implements android.view.animation.Interpolator { public class PathInterpolator extends android.view.animation.BaseInterpolator { ctor public PathInterpolator(android.graphics.Path); ctor public PathInterpolator(float, float); ctor public PathInterpolator(float, float, float, float);
core/java/android/animation/Animator.java +103 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.animation; import android.content.res.ConstantState; import java.util.ArrayList; /** Loading @@ -40,6 +42,18 @@ public abstract class Animator implements Cloneable { */ boolean mPaused = false; /** * A set of flags which identify the type of configuration changes that can affect this * Animator. Used by the Animator cache. */ int mChangingConfigurations = 0; /** * If this animator is inflated from a constant state, keep a reference to it so that * ConstantState will not be garbage collected until this animator is collected */ private AnimatorConstantState mConstantState; /** * Starts this animation. If the animation has a nonzero startDelay, the animation will start * running after that delay elapses. A non-delayed animation will have its initial Loading Loading @@ -295,25 +309,71 @@ public abstract class Animator implements Cloneable { } } /** * Return a mask of the configuration parameters for which this animator may change, requiring * that it should be re-created from Resources. The default implementation returns whatever * value was provided through setChangingConfigurations(int) or 0 by default. * * @return Returns a mask of the changing configuration parameters, as defined by * {@link android.content.pm.ActivityInfo}. * @see android.content.pm.ActivityInfo * @hide */ public int getChangingConfigurations() { return mChangingConfigurations; } /** * Set a mask of the configuration parameters for which this animator may change, requiring * that it be re-created from resource. * * @param configs A mask of the changing configuration parameters, as * defined by {@link android.content.pm.ActivityInfo}. * * @see android.content.pm.ActivityInfo * @hide */ public void setChangingConfigurations(int configs) { mChangingConfigurations = configs; } /** * Sets the changing configurations value to the union of the current changing configurations * and the provided configs. * This method is called while loading the animator. * @hide */ public void appendChangingConfigurations(int configs) { mChangingConfigurations |= configs; } /** * Return a {@link android.content.res.ConstantState} instance that holds the shared state of * this Animator. * <p> * This constant state is used to create new instances of this animator when needed, instead * of re-loading it from resources. Default implementation creates a new * {@link AnimatorConstantState}. You can override this method to provide your custom logic or * return null if you don't want this animator to be cached. * * @return The ConfigurationBoundResourceCache.BaseConstantState associated to this Animator. * @see android.content.res.ConstantState * @see #clone() * @hide */ public ConstantState<Animator> createConstantState() { return new AnimatorConstantState(this); } @Override public Animator clone() { try { final Animator anim = (Animator) super.clone(); if (mListeners != null) { ArrayList<AnimatorListener> oldListeners = mListeners; anim.mListeners = new ArrayList<AnimatorListener>(); int numListeners = oldListeners.size(); for (int i = 0; i < numListeners; ++i) { anim.mListeners.add(oldListeners.get(i)); } anim.mListeners = new ArrayList<AnimatorListener>(mListeners); } if (mPauseListeners != null) { ArrayList<AnimatorPauseListener> oldListeners = mPauseListeners; anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(); int numListeners = oldListeners.size(); for (int i = 0; i < numListeners; ++i) { anim.mPauseListeners.add(oldListeners.get(i)); } anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners); } return anim; } catch (CloneNotSupportedException e) { Loading Loading @@ -469,4 +529,35 @@ public abstract class Animator implements Cloneable { public void setAllowRunningAsynchronously(boolean mayRunAsync) { // It is up to subclasses to support this, if they can. } /** * Creates a {@link ConstantState} which holds changing configurations information associated * with the given Animator. * <p> * When {@link #newInstance()} is called, default implementation clones the Animator. */ private static class AnimatorConstantState extends ConstantState<Animator> { final Animator mAnimator; int mChangingConf; public AnimatorConstantState(Animator animator) { mAnimator = animator; // ensure a reference back to here so that constante state is not gc'ed. mAnimator.mConstantState = this; mChangingConf = mAnimator.getChangingConfigurations(); } @Override public int getChangingConfigurations() { return mChangingConf; } @Override public Animator newInstance() { final Animator clone = mAnimator.clone(); clone.mConstantState = this; return clone; } } }
core/java/android/animation/AnimatorInflater.java +73 −17 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.animation; import android.content.Context; import android.content.res.ConfigurationBoundResourceCache; import android.content.res.ConstantState; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.content.res.Resources.Theme; Loading @@ -30,6 +32,8 @@ import android.util.TypedValue; import android.util.Xml; import android.view.InflateException; import android.view.animation.AnimationUtils; import android.view.animation.BaseInterpolator; import android.view.animation.Interpolator; import com.android.internal.R; Loading Loading @@ -67,6 +71,9 @@ public class AnimatorInflater { private static final boolean DBG_ANIMATOR_INFLATER = false; // used to calculate changing configs for resource references private static final TypedValue sTmpTypedValue = new TypedValue(); /** * Loads an {@link Animator} object from a resource * Loading Loading @@ -98,11 +105,34 @@ public class AnimatorInflater { /** @hide */ public static Animator loadAnimator(Resources resources, Theme theme, int id, float pathErrorScale) throws NotFoundException { final ConfigurationBoundResourceCache<Animator> animatorCache = resources .getAnimatorCache(); Animator animator = animatorCache.get(id, theme); if (animator != null) { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id)); } return animator; } else if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "cache miss for animator " + resources.getResourceName(id)); } XmlResourceParser parser = null; try { parser = resources.getAnimation(id); return createAnimatorFromXml(resources, theme, parser, pathErrorScale); animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<Animator> constantState = animator.createConstantState(); if (constantState != null) { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "caching animator for res " + resources.getResourceName(id)); } animatorCache.put(id, theme, constantState); // create a new animator so that cached version is never used by the user animator = constantState.newInstance(resources, theme); } } return animator; } catch (XmlPullParserException ex) { Resources.NotFoundException rnf = new Resources.NotFoundException("Can't load animation resource ID #0x" + Loading @@ -122,10 +152,29 @@ public class AnimatorInflater { public static StateListAnimator loadStateListAnimator(Context context, int id) throws NotFoundException { final Resources resources = context.getResources(); final ConfigurationBoundResourceCache<StateListAnimator> cache = resources .getStateListAnimatorCache(); final Theme theme = context.getTheme(); StateListAnimator animator = cache.get(id, theme); if (animator != null) { return animator; } XmlResourceParser parser = null; try { parser = context.getResources().getAnimation(id); return createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); parser = resources.getAnimation(id); animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<StateListAnimator> constantState = animator .createConstantState(); if (constantState != null) { cache.put(id, theme, constantState); // return a clone so that the animator in constant state is never used. animator = constantState.newInstance(resources, theme); } } return animator; } catch (XmlPullParserException ex) { Resources.NotFoundException rnf = new Resources.NotFoundException( Loading Loading @@ -172,14 +221,13 @@ public class AnimatorInflater { for (int i = 0; i < attributeCount; i++) { int attrName = attributeSet.getAttributeNameResource(i); if (attrName == R.attr.animation) { animator = loadAnimator(context, attributeSet.getAttributeResourceValue(i, 0)); final int animId = attributeSet.getAttributeResourceValue(i, 0); animator = loadAnimator(context, animId); } else { states[stateIndex++] = attributeSet.getAttributeBooleanValue(i, false) ? attrName : -attrName; } } if (animator == null) { animator = createAnimatorFromXml(context.getResources(), Loading @@ -192,7 +240,6 @@ public class AnimatorInflater { } stateListAnimator .addState(StateSet.trimStateSet(states, stateIndex), animator); } break; } Loading Loading @@ -508,7 +555,6 @@ public class AnimatorInflater { private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser, AttributeSet attrs, AnimatorSet parent, int sequenceOrdering, float pixelSize) throws XmlPullParserException, IOException { Animator anim = null; ArrayList<Animator> childAnims = null; Loading Loading @@ -537,8 +583,8 @@ public class AnimatorInflater { } else { a = res.obtainAttributes(attrs, R.styleable.AnimatorSet); } int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER); anim.appendChangingConfigurations(a.getChangingConfigurations()); int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER); createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering, pixelSize); a.recycle(); Loading @@ -565,7 +611,6 @@ public class AnimatorInflater { parent.playSequentially(animsArray); } } return anim; } Loading @@ -591,7 +636,6 @@ public class AnimatorInflater { private static ValueAnimator loadAnimator(Resources res, Theme theme, AttributeSet attrs, ValueAnimator anim, float pathErrorScale) throws NotFoundException { TypedArray arrayAnimator = null; TypedArray arrayObjectAnimator = null; Loading @@ -609,25 +653,37 @@ public class AnimatorInflater { } else { arrayObjectAnimator = res.obtainAttributes(attrs, R.styleable.PropertyAnimator); } anim.appendChangingConfigurations(arrayObjectAnimator.getChangingConfigurations()); } if (anim == null) { anim = new ValueAnimator(); } anim.appendChangingConfigurations(arrayAnimator.getChangingConfigurations()); parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator, pathErrorScale); final int resID = arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0); final int resID = arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0); if (resID > 0) { anim.setInterpolator(AnimationUtils.loadInterpolator(res, theme, resID)); final Interpolator interpolator = AnimationUtils.loadInterpolator(res, theme, resID); if (interpolator instanceof BaseInterpolator) { anim.appendChangingConfigurations( ((BaseInterpolator) interpolator).getChangingConfiguration()); } anim.setInterpolator(interpolator); } arrayAnimator.recycle(); if (arrayObjectAnimator != null) { arrayObjectAnimator.recycle(); } return anim; } private static int getChangingConfigs(Resources resources, int id) { synchronized (sTmpTypedValue) { resources.getValue(id, sTmpTypedValue, true); return sTmpTypedValue.changingConfigurations; } } }
core/java/android/animation/AnimatorSet.java +54 −24 Original line number Diff line number Diff line Loading @@ -240,6 +240,19 @@ public final class AnimatorSet extends Animator { } } /** * @hide */ @Override public int getChangingConfigurations() { int conf = super.getChangingConfigurations(); final int nodeCount = mNodes.size(); for (int i = 0; i < nodeCount; i ++) { conf |= mNodes.get(i).animation.getChangingConfigurations(); } return conf; } /** * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations} * of this AnimatorSet. The default value is null, which means that no interpolator Loading Loading @@ -628,23 +641,25 @@ public final class AnimatorSet extends Animator { * manually, as we clone each Node (and its animation). The clone will then be sorted, * and will populate any appropriate lists, when it is started. */ final int nodeCount = mNodes.size(); anim.mNeedsSort = true; anim.mTerminated = false; anim.mStarted = false; anim.mPlayingSet = new ArrayList<Animator>(); anim.mNodeMap = new HashMap<Animator, Node>(); anim.mNodes = new ArrayList<Node>(); anim.mSortedNodes = new ArrayList<Node>(); anim.mNodes = new ArrayList<Node>(nodeCount); anim.mSortedNodes = new ArrayList<Node>(nodeCount); anim.mReversible = mReversible; anim.mSetListener = null; // 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 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) { for (int n = 0; n < nodeCount; n++) { final Node node = mNodes.get(n); Node nodeClone = node.clone(); nodeCloneMap.put(node, nodeClone); node.mTmpClone = nodeClone; anim.mNodes.add(nodeClone); anim.mNodeMap.put(nodeClone.animation, nodeClone); // Clear out the dependencies in the clone; we'll set these up manually later Loading @@ -652,40 +667,50 @@ public final class AnimatorSet extends Animator { nodeClone.tmpDependencies = null; nodeClone.nodeDependents = null; nodeClone.nodeDependencies = null; // clear out any listeners that were set up by the AnimatorSet; these will // be set up when the clone's nodes are sorted ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); final ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); if (cloneListeners != null) { ArrayList<AnimatorListener> listenersToRemove = null; for (AnimatorListener listener : cloneListeners) { for (int i = cloneListeners.size() - 1; i >= 0; i--) { final AnimatorListener listener = cloneListeners.get(i); if (listener instanceof AnimatorSetListener) { if (listenersToRemove == null) { listenersToRemove = new ArrayList<AnimatorListener>(); } listenersToRemove.add(listener); } } if (listenersToRemove != null) { for (AnimatorListener listener : listenersToRemove) { cloneListeners.remove(listener); cloneListeners.remove(i); } } } } // Now that we've cloned all of the nodes, we're ready to walk through their // dependencies, mapping the old dependencies to the new nodes for (Node node : mNodes) { Node nodeClone = nodeCloneMap.get(node); for (int n = 0; n < nodeCount; n++) { final Node node = mNodes.get(n); final Node clone = node.mTmpClone; if (node.dependencies != null) { for (Dependency dependency : node.dependencies) { Node clonedDependencyNode = nodeCloneMap.get(dependency.node); Dependency cloneDependency = new Dependency(clonedDependencyNode, clone.dependencies = new ArrayList<Dependency>(node.dependencies.size()); final int depSize = node.dependencies.size(); for (int i = 0; i < depSize; i ++) { final Dependency dependency = node.dependencies.get(i); Dependency cloneDependency = new Dependency(dependency.node.mTmpClone, dependency.rule); nodeClone.addDependency(cloneDependency); clone.dependencies.add(cloneDependency); } } if (node.nodeDependents != null) { clone.nodeDependents = new ArrayList<Node>(node.nodeDependents.size()); for (Node dep : node.nodeDependents) { clone.nodeDependents.add(dep.mTmpClone); } } if (node.nodeDependencies != null) { clone.nodeDependencies = new ArrayList<Node>(node.nodeDependencies.size()); for (Node dep : node.nodeDependencies) { clone.nodeDependencies.add(dep.mTmpClone); } } } for (int n = 0; n < nodeCount; n++) { mNodes.get(n).mTmpClone = null; } return anim; } Loading Loading @@ -1016,6 +1041,11 @@ public final class AnimatorSet extends Animator { */ public boolean done = false; /** * Temporary field to hold the clone in AnimatorSet#clone. Cleaned after clone is complete */ private Node mTmpClone = null; /** * Constructs the Node with the animation that it encapsulates. A Node has no * dependencies by default; dependencies are added via the addDependency() Loading
core/java/android/animation/FloatKeyframeSet.java +3 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.animation; import android.animation.Keyframe.FloatKeyframe; import java.util.ArrayList; import java.util.List; /** * This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate Loading Loading @@ -47,8 +48,8 @@ class FloatKeyframeSet extends KeyframeSet implements Keyframes.FloatKeyframes { @Override public FloatKeyframeSet clone() { ArrayList<Keyframe> keyframes = mKeyframes; int numKeyframes = mKeyframes.size(); final List<Keyframe> keyframes = mKeyframes; final int numKeyframes = mKeyframes.size(); FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes]; for (int i = 0; i < numKeyframes; ++i) { newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone(); Loading