Loading graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +208 −65 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.animation.AnimatorInflater; import android.animation.AnimatorSet; import android.animation.Animator.AnimatorListener; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; Loading Loading @@ -128,15 +129,27 @@ import java.util.List; * @attr ref android.R.styleable#AnimatedVectorDrawableTarget_animation */ public class AnimatedVectorDrawable extends Drawable implements Animatable { private static final String LOGTAG = AnimatedVectorDrawable.class.getSimpleName(); private static final String LOGTAG = "AnimatedVectorDrawable"; private static final String ANIMATED_VECTOR = "animated-vector"; private static final String TARGET = "target"; private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false; /** Local, mutable animator set. */ private final AnimatorSet mAnimatorSet = new AnimatorSet(); /** * The resources against which this drawable was created. Used to attempt * to inflate animators if applyTheme() doesn't get called. */ private Resources mRes; private AnimatedVectorDrawableState mAnimatedVectorState; /** Whether the animator set has been prepared. */ private boolean mHasAnimatorSet; private boolean mMutated; public AnimatedVectorDrawable() { Loading @@ -145,6 +158,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res) { mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res); mRes = res; } @Override Loading @@ -162,7 +176,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { */ public void clearMutated() { super.clearMutated(); if (mAnimatedVectorState.mVectorDrawable != null) { mAnimatedVectorState.mVectorDrawable.clearMutated(); } mMutated = false; } Loading Loading @@ -274,6 +290,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { @Override public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { final AnimatedVectorDrawableState state = mAnimatedVectorState; int eventType = parser.getEventType(); float pathErrorScale = 1; Loading @@ -291,10 +308,10 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { vectorDrawable.setAllowCaching(false); vectorDrawable.setCallback(mCallback); pathErrorScale = vectorDrawable.getPixelSize(); if (mAnimatedVectorState.mVectorDrawable != null) { mAnimatedVectorState.mVectorDrawable.setCallback(null); if (state.mVectorDrawable != null) { state.mVectorDrawable.setCallback(null); } mAnimatedVectorState.mVectorDrawable = vectorDrawable; state.mVectorDrawable = vectorDrawable; } a.recycle(); } else if (TARGET.equals(tagName)) { Loading @@ -302,13 +319,21 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { R.styleable.AnimatedVectorDrawableTarget); final String target = a.getString( R.styleable.AnimatedVectorDrawableTarget_name); int id = a.getResourceId( final int animResId = a.getResourceId( R.styleable.AnimatedVectorDrawableTarget_animation, 0); if (id != 0) { Animator objectAnimator = AnimatorInflater.loadAnimator(res, theme, id, pathErrorScale); setupAnimatorsForTarget(target, objectAnimator); if (animResId != 0) { if (theme != null) { final Animator objectAnimator = AnimatorInflater.loadAnimator( res, theme, animResId, pathErrorScale); state.addTargetAnimator(target, objectAnimator); } else { // The animation may be theme-dependent. As a // workaround until Animator has full support for // applyTheme(), postpone loading the animator // until we have a theme in applyTheme(). state.addPendingAnimator(animResId, pathErrorScale, target); } } a.recycle(); } Loading @@ -316,15 +341,10 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { eventType = parser.next(); } setupAnimatorSet(); } private void setupAnimatorSet() { if (mAnimatedVectorState.mTempAnimators != null) { mAnimatedVectorState.mAnimatorSet.playTogether(mAnimatedVectorState.mTempAnimators); mAnimatedVectorState.mTempAnimators.clear(); mAnimatedVectorState.mTempAnimators = null; } // If we don't have any pending animations, we don't need to hold a // reference to the resources. mRes = state.mPendingAnims == null ? null : res; } @Override Loading @@ -341,6 +361,16 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { if (vectorDrawable != null && vectorDrawable.canApplyTheme()) { vectorDrawable.applyTheme(t); } if (t != null) { mAnimatedVectorState.inflatePendingAnimators(t.getResources(), t); } // If we don't have any pending animations, we don't need to hold a // reference to the resources. if (mAnimatedVectorState.mPendingAnims == null) { mRes = null; } } /** Loading @@ -350,7 +380,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { * @param listener the listener to be added to the current set of listeners for this animation. */ public void addListener(AnimatorListener listener) { mAnimatedVectorState.mAnimatorSet.addListener(listener); mAnimatorSet.addListener(listener); } /** Loading @@ -360,7 +390,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { * animation. */ public void removeListener(AnimatorListener listener) { mAnimatedVectorState.mAnimatorSet.removeListener(listener); mAnimatorSet.removeListener(listener); } /** Loading @@ -370,23 +400,27 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { * @return List<AnimatorListener> The set of listeners. */ public List<AnimatorListener> getListeners() { return mAnimatedVectorState.mAnimatorSet.getListeners(); return mAnimatorSet.getListeners(); } private static class AnimatedVectorDrawableState extends ConstantState { int mChangingConfigurations; VectorDrawable mVectorDrawable; // Always have a valid animatorSet to handle all the listeners call. AnimatorSet mAnimatorSet = new AnimatorSet(); // When parsing the XML, we build individual animator and store in this array. At the end, // we add this array into the mAnimatorSet. private ArrayList<Animator> mTempAnimators; /** Animators that require a theme before inflation. */ ArrayList<PendingAnimator> mPendingAnims; /** Fully inflated animators awaiting cloning into an AnimatorSet. */ ArrayList<Animator> mAnimators; /** Map of animators to their target object names */ ArrayMap<Animator, String> mTargetNameMap; public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy, Callback owner, Resources res) { if (copy != null) { mChangingConfigurations = copy.mChangingConfigurations; if (copy.mVectorDrawable != null) { final ConstantState cs = copy.mVectorDrawable.getConstantState(); if (res != null) { Loading @@ -400,24 +434,17 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds()); mVectorDrawable.setAllowCaching(false); } if (copy.mAnimatorSet != null) { final int numAnimators = copy.mTargetNameMap.size(); // Deep copy a animator set, and then setup the target map again. mAnimatorSet = copy.mAnimatorSet.clone(); mTargetNameMap = new ArrayMap<Animator, String>(numAnimators); // Since the new AnimatorSet is cloned from the old one, the order must be the // same inside the array. ArrayList<Animator> oldAnim = copy.mAnimatorSet.getChildAnimations(); ArrayList<Animator> newAnim = mAnimatorSet.getChildAnimations(); for (int i = 0; i < numAnimators; ++i) { // Target name must be the same for new and old String targetName = copy.mTargetNameMap.get(oldAnim.get(i)); if (copy.mAnimators != null) { mAnimators = new ArrayList<>(copy.mAnimators); } Object newTargetObject = mVectorDrawable.getTargetByName(targetName); newAnim.get(i).setTarget(newTargetObject); mTargetNameMap.put(newAnim.get(i), targetName); if (copy.mTargetNameMap != null) { mTargetNameMap = new ArrayMap<>(copy.mTargetNameMap); } if (copy.mPendingAnims != null) { mPendingAnims = new ArrayList<>(copy.mPendingAnims); } } else { mVectorDrawable = new VectorDrawable(); Loading @@ -427,7 +454,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { @Override public boolean canApplyTheme() { return (mVectorDrawable != null && mVectorDrawable.canApplyTheme()) || super.canApplyTheme(); || mPendingAnims != null || super.canApplyTheme(); } @Override Loading @@ -444,44 +471,157 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { public int getChangingConfigurations() { return mChangingConfigurations; } public void addPendingAnimator(int resId, float pathErrorScale, String target) { if (mPendingAnims == null) { mPendingAnims = new ArrayList<>(1); } mPendingAnims.add(new PendingAnimator(resId, pathErrorScale, target)); } private void setupAnimatorsForTarget(String name, Animator animator) { Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name); animator.setTarget(target); if (mAnimatedVectorState.mTempAnimators == null) { mAnimatedVectorState.mTempAnimators = new ArrayList<Animator>(); mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>(); public void addTargetAnimator(String targetName, Animator animator) { if (mAnimators == null) { mAnimators = new ArrayList<>(1); mTargetNameMap = new ArrayMap<>(1); } mAnimatedVectorState.mTempAnimators.add(animator); mAnimatedVectorState.mTargetNameMap.put(animator, name); mAnimators.add(animator); mTargetNameMap.put(animator, targetName); if (DBG_ANIMATION_VECTOR_DRAWABLE) { Log.v(LOGTAG, "add animator for target " + name + " " + animator); Log.v(LOGTAG, "add animator for target " + targetName + " " + animator); } } /** * Prepares a local set of mutable animators based on the constant * state. * <p> * If there are any pending uninflated animators, attempts to inflate * them immediately against the provided resources object. * * @param animatorSet the animator set to which the animators should * be added * @param res the resources against which to inflate any pending * animators, or {@code null} if not available */ public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet, @Nullable Resources res) { // Check for uninflated animators. We can remove this after we add // support for Animator.applyTheme(). See comments in inflate(). if (mPendingAnims != null) { // Attempt to load animators without applying a theme. if (res != null) { inflatePendingAnimators(res, null); } else { Log.e(LOGTAG, "Failed to load animators. Either the AnimatedVectorDrawable" + " must be created using a Resources object or applyTheme() must be" + " called with a non-null Theme object."); } mPendingAnims = null; } // Perform a deep copy of the constant state's animators. final int count = mAnimators == null ? 0 : mAnimators.size(); if (count > 0) { final Animator firstAnim = prepareLocalAnimator(0); final AnimatorSet.Builder builder = animatorSet.play(firstAnim); for (int i = 1; i < count; ++i) { final Animator nextAnim = prepareLocalAnimator(i); builder.with(nextAnim); } } } /** * Prepares a local animator for the given index within the constant * state's list of animators. * * @param index the index of the animator within the constant state */ private Animator prepareLocalAnimator(int index) { final Animator animator = mAnimators.get(index); final Animator localAnimator = animator.clone(); final String targetName = mTargetNameMap.get(animator); final Object target = mVectorDrawable.getTargetByName(targetName); localAnimator.setTarget(target); return localAnimator; } /** * Inflates pending animators, if any, against a theme. Clears the list of * pending animators. * * @param t the theme against which to inflate the animators */ public void inflatePendingAnimators(@NonNull Resources res, @Nullable Theme t) { final ArrayList<PendingAnimator> pendingAnims = mPendingAnims; if (pendingAnims != null) { mPendingAnims = null; for (int i = 0, count = pendingAnims.size(); i < count; i++) { final PendingAnimator pendingAnimator = pendingAnims.get(i); final Animator objectAnimator = pendingAnimator.newInstance(res, t); addTargetAnimator(pendingAnimator.target, objectAnimator); } } } /** * Basically a constant state for Animators until we actually implement * constant states for Animators. */ private static class PendingAnimator { public final int animResId; public final float pathErrorScale; public final String target; public PendingAnimator(int animResId, float pathErrorScale, String target) { this.animResId = animResId; this.pathErrorScale = pathErrorScale; this.target = target; } public Animator newInstance(Resources res, Theme theme) { return AnimatorInflater.loadAnimator(res, theme, animResId, pathErrorScale); } } } @Override public boolean isRunning() { return mAnimatedVectorState.mAnimatorSet.isRunning(); return mAnimatorSet.isRunning(); } private boolean isStarted() { return mAnimatedVectorState.mAnimatorSet.isStarted(); return mAnimatorSet.isStarted(); } @Override public void start() { ensureAnimatorSet(); // If any one of the animator has not ended, do nothing. if (isStarted()) { return; } mAnimatedVectorState.mAnimatorSet.start(); mAnimatorSet.start(); invalidateSelf(); } @NonNull private void ensureAnimatorSet() { if (!mHasAnimatorSet) { mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes); mHasAnimatorSet = true; mRes = null; } } @Override public void stop() { mAnimatedVectorState.mAnimatorSet.end(); mAnimatorSet.end(); } /** Loading @@ -492,20 +632,23 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { * @hide */ public void reverse() { // Only reverse when all the animators can be reverse. Otherwise, partially // reverse is confusing. ensureAnimatorSet(); // Only reverse when all the animators can be reversed. if (!canReverse()) { Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()"); return; } mAnimatedVectorState.mAnimatorSet.reverse(); mAnimatorSet.reverse(); invalidateSelf(); } /** * @hide */ public boolean canReverse() { return mAnimatedVectorState.mAnimatorSet.canReverse(); return mAnimatorSet.canReverse(); } private final Callback mCallback = new Callback() { Loading Loading
graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +208 −65 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.animation.AnimatorInflater; import android.animation.AnimatorSet; import android.animation.Animator.AnimatorListener; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; Loading Loading @@ -128,15 +129,27 @@ import java.util.List; * @attr ref android.R.styleable#AnimatedVectorDrawableTarget_animation */ public class AnimatedVectorDrawable extends Drawable implements Animatable { private static final String LOGTAG = AnimatedVectorDrawable.class.getSimpleName(); private static final String LOGTAG = "AnimatedVectorDrawable"; private static final String ANIMATED_VECTOR = "animated-vector"; private static final String TARGET = "target"; private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false; /** Local, mutable animator set. */ private final AnimatorSet mAnimatorSet = new AnimatorSet(); /** * The resources against which this drawable was created. Used to attempt * to inflate animators if applyTheme() doesn't get called. */ private Resources mRes; private AnimatedVectorDrawableState mAnimatedVectorState; /** Whether the animator set has been prepared. */ private boolean mHasAnimatorSet; private boolean mMutated; public AnimatedVectorDrawable() { Loading @@ -145,6 +158,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res) { mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res); mRes = res; } @Override Loading @@ -162,7 +176,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { */ public void clearMutated() { super.clearMutated(); if (mAnimatedVectorState.mVectorDrawable != null) { mAnimatedVectorState.mVectorDrawable.clearMutated(); } mMutated = false; } Loading Loading @@ -274,6 +290,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { @Override public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { final AnimatedVectorDrawableState state = mAnimatedVectorState; int eventType = parser.getEventType(); float pathErrorScale = 1; Loading @@ -291,10 +308,10 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { vectorDrawable.setAllowCaching(false); vectorDrawable.setCallback(mCallback); pathErrorScale = vectorDrawable.getPixelSize(); if (mAnimatedVectorState.mVectorDrawable != null) { mAnimatedVectorState.mVectorDrawable.setCallback(null); if (state.mVectorDrawable != null) { state.mVectorDrawable.setCallback(null); } mAnimatedVectorState.mVectorDrawable = vectorDrawable; state.mVectorDrawable = vectorDrawable; } a.recycle(); } else if (TARGET.equals(tagName)) { Loading @@ -302,13 +319,21 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { R.styleable.AnimatedVectorDrawableTarget); final String target = a.getString( R.styleable.AnimatedVectorDrawableTarget_name); int id = a.getResourceId( final int animResId = a.getResourceId( R.styleable.AnimatedVectorDrawableTarget_animation, 0); if (id != 0) { Animator objectAnimator = AnimatorInflater.loadAnimator(res, theme, id, pathErrorScale); setupAnimatorsForTarget(target, objectAnimator); if (animResId != 0) { if (theme != null) { final Animator objectAnimator = AnimatorInflater.loadAnimator( res, theme, animResId, pathErrorScale); state.addTargetAnimator(target, objectAnimator); } else { // The animation may be theme-dependent. As a // workaround until Animator has full support for // applyTheme(), postpone loading the animator // until we have a theme in applyTheme(). state.addPendingAnimator(animResId, pathErrorScale, target); } } a.recycle(); } Loading @@ -316,15 +341,10 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { eventType = parser.next(); } setupAnimatorSet(); } private void setupAnimatorSet() { if (mAnimatedVectorState.mTempAnimators != null) { mAnimatedVectorState.mAnimatorSet.playTogether(mAnimatedVectorState.mTempAnimators); mAnimatedVectorState.mTempAnimators.clear(); mAnimatedVectorState.mTempAnimators = null; } // If we don't have any pending animations, we don't need to hold a // reference to the resources. mRes = state.mPendingAnims == null ? null : res; } @Override Loading @@ -341,6 +361,16 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { if (vectorDrawable != null && vectorDrawable.canApplyTheme()) { vectorDrawable.applyTheme(t); } if (t != null) { mAnimatedVectorState.inflatePendingAnimators(t.getResources(), t); } // If we don't have any pending animations, we don't need to hold a // reference to the resources. if (mAnimatedVectorState.mPendingAnims == null) { mRes = null; } } /** Loading @@ -350,7 +380,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { * @param listener the listener to be added to the current set of listeners for this animation. */ public void addListener(AnimatorListener listener) { mAnimatedVectorState.mAnimatorSet.addListener(listener); mAnimatorSet.addListener(listener); } /** Loading @@ -360,7 +390,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { * animation. */ public void removeListener(AnimatorListener listener) { mAnimatedVectorState.mAnimatorSet.removeListener(listener); mAnimatorSet.removeListener(listener); } /** Loading @@ -370,23 +400,27 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { * @return List<AnimatorListener> The set of listeners. */ public List<AnimatorListener> getListeners() { return mAnimatedVectorState.mAnimatorSet.getListeners(); return mAnimatorSet.getListeners(); } private static class AnimatedVectorDrawableState extends ConstantState { int mChangingConfigurations; VectorDrawable mVectorDrawable; // Always have a valid animatorSet to handle all the listeners call. AnimatorSet mAnimatorSet = new AnimatorSet(); // When parsing the XML, we build individual animator and store in this array. At the end, // we add this array into the mAnimatorSet. private ArrayList<Animator> mTempAnimators; /** Animators that require a theme before inflation. */ ArrayList<PendingAnimator> mPendingAnims; /** Fully inflated animators awaiting cloning into an AnimatorSet. */ ArrayList<Animator> mAnimators; /** Map of animators to their target object names */ ArrayMap<Animator, String> mTargetNameMap; public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy, Callback owner, Resources res) { if (copy != null) { mChangingConfigurations = copy.mChangingConfigurations; if (copy.mVectorDrawable != null) { final ConstantState cs = copy.mVectorDrawable.getConstantState(); if (res != null) { Loading @@ -400,24 +434,17 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds()); mVectorDrawable.setAllowCaching(false); } if (copy.mAnimatorSet != null) { final int numAnimators = copy.mTargetNameMap.size(); // Deep copy a animator set, and then setup the target map again. mAnimatorSet = copy.mAnimatorSet.clone(); mTargetNameMap = new ArrayMap<Animator, String>(numAnimators); // Since the new AnimatorSet is cloned from the old one, the order must be the // same inside the array. ArrayList<Animator> oldAnim = copy.mAnimatorSet.getChildAnimations(); ArrayList<Animator> newAnim = mAnimatorSet.getChildAnimations(); for (int i = 0; i < numAnimators; ++i) { // Target name must be the same for new and old String targetName = copy.mTargetNameMap.get(oldAnim.get(i)); if (copy.mAnimators != null) { mAnimators = new ArrayList<>(copy.mAnimators); } Object newTargetObject = mVectorDrawable.getTargetByName(targetName); newAnim.get(i).setTarget(newTargetObject); mTargetNameMap.put(newAnim.get(i), targetName); if (copy.mTargetNameMap != null) { mTargetNameMap = new ArrayMap<>(copy.mTargetNameMap); } if (copy.mPendingAnims != null) { mPendingAnims = new ArrayList<>(copy.mPendingAnims); } } else { mVectorDrawable = new VectorDrawable(); Loading @@ -427,7 +454,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { @Override public boolean canApplyTheme() { return (mVectorDrawable != null && mVectorDrawable.canApplyTheme()) || super.canApplyTheme(); || mPendingAnims != null || super.canApplyTheme(); } @Override Loading @@ -444,44 +471,157 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { public int getChangingConfigurations() { return mChangingConfigurations; } public void addPendingAnimator(int resId, float pathErrorScale, String target) { if (mPendingAnims == null) { mPendingAnims = new ArrayList<>(1); } mPendingAnims.add(new PendingAnimator(resId, pathErrorScale, target)); } private void setupAnimatorsForTarget(String name, Animator animator) { Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name); animator.setTarget(target); if (mAnimatedVectorState.mTempAnimators == null) { mAnimatedVectorState.mTempAnimators = new ArrayList<Animator>(); mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>(); public void addTargetAnimator(String targetName, Animator animator) { if (mAnimators == null) { mAnimators = new ArrayList<>(1); mTargetNameMap = new ArrayMap<>(1); } mAnimatedVectorState.mTempAnimators.add(animator); mAnimatedVectorState.mTargetNameMap.put(animator, name); mAnimators.add(animator); mTargetNameMap.put(animator, targetName); if (DBG_ANIMATION_VECTOR_DRAWABLE) { Log.v(LOGTAG, "add animator for target " + name + " " + animator); Log.v(LOGTAG, "add animator for target " + targetName + " " + animator); } } /** * Prepares a local set of mutable animators based on the constant * state. * <p> * If there are any pending uninflated animators, attempts to inflate * them immediately against the provided resources object. * * @param animatorSet the animator set to which the animators should * be added * @param res the resources against which to inflate any pending * animators, or {@code null} if not available */ public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet, @Nullable Resources res) { // Check for uninflated animators. We can remove this after we add // support for Animator.applyTheme(). See comments in inflate(). if (mPendingAnims != null) { // Attempt to load animators without applying a theme. if (res != null) { inflatePendingAnimators(res, null); } else { Log.e(LOGTAG, "Failed to load animators. Either the AnimatedVectorDrawable" + " must be created using a Resources object or applyTheme() must be" + " called with a non-null Theme object."); } mPendingAnims = null; } // Perform a deep copy of the constant state's animators. final int count = mAnimators == null ? 0 : mAnimators.size(); if (count > 0) { final Animator firstAnim = prepareLocalAnimator(0); final AnimatorSet.Builder builder = animatorSet.play(firstAnim); for (int i = 1; i < count; ++i) { final Animator nextAnim = prepareLocalAnimator(i); builder.with(nextAnim); } } } /** * Prepares a local animator for the given index within the constant * state's list of animators. * * @param index the index of the animator within the constant state */ private Animator prepareLocalAnimator(int index) { final Animator animator = mAnimators.get(index); final Animator localAnimator = animator.clone(); final String targetName = mTargetNameMap.get(animator); final Object target = mVectorDrawable.getTargetByName(targetName); localAnimator.setTarget(target); return localAnimator; } /** * Inflates pending animators, if any, against a theme. Clears the list of * pending animators. * * @param t the theme against which to inflate the animators */ public void inflatePendingAnimators(@NonNull Resources res, @Nullable Theme t) { final ArrayList<PendingAnimator> pendingAnims = mPendingAnims; if (pendingAnims != null) { mPendingAnims = null; for (int i = 0, count = pendingAnims.size(); i < count; i++) { final PendingAnimator pendingAnimator = pendingAnims.get(i); final Animator objectAnimator = pendingAnimator.newInstance(res, t); addTargetAnimator(pendingAnimator.target, objectAnimator); } } } /** * Basically a constant state for Animators until we actually implement * constant states for Animators. */ private static class PendingAnimator { public final int animResId; public final float pathErrorScale; public final String target; public PendingAnimator(int animResId, float pathErrorScale, String target) { this.animResId = animResId; this.pathErrorScale = pathErrorScale; this.target = target; } public Animator newInstance(Resources res, Theme theme) { return AnimatorInflater.loadAnimator(res, theme, animResId, pathErrorScale); } } } @Override public boolean isRunning() { return mAnimatedVectorState.mAnimatorSet.isRunning(); return mAnimatorSet.isRunning(); } private boolean isStarted() { return mAnimatedVectorState.mAnimatorSet.isStarted(); return mAnimatorSet.isStarted(); } @Override public void start() { ensureAnimatorSet(); // If any one of the animator has not ended, do nothing. if (isStarted()) { return; } mAnimatedVectorState.mAnimatorSet.start(); mAnimatorSet.start(); invalidateSelf(); } @NonNull private void ensureAnimatorSet() { if (!mHasAnimatorSet) { mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes); mHasAnimatorSet = true; mRes = null; } } @Override public void stop() { mAnimatedVectorState.mAnimatorSet.end(); mAnimatorSet.end(); } /** Loading @@ -492,20 +632,23 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { * @hide */ public void reverse() { // Only reverse when all the animators can be reverse. Otherwise, partially // reverse is confusing. ensureAnimatorSet(); // Only reverse when all the animators can be reversed. if (!canReverse()) { Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()"); return; } mAnimatedVectorState.mAnimatorSet.reverse(); mAnimatorSet.reverse(); invalidateSelf(); } /** * @hide */ public boolean canReverse() { return mAnimatedVectorState.mAnimatorSet.canReverse(); return mAnimatorSet.canReverse(); } private final Callback mCallback = new Callback() { Loading