Use constant state in AnimatedVectorDrawable
Complex animated vector drawables can be expensive to load due to
sub-optimal parsing of the String-based pathData. Suffering that penalty
every time the same drawable is loaded (such as material-themed
ProgressBars) is painful.
The new approach caches constant state of both the VectorDrawable (including
the pathData geometry) and the animators (which includes potentially expensive
path-based interpolators).
issue #17366831 Material ProgressBar taking 200+ms to inflate
Change-Id: Iba3b541e24cfce8c07f5aa9fe6aa7d7b92b2fe1c
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 7c13dbe..0aa8fdd 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -636,6 +636,7 @@
anim.mNodes = new ArrayList<Node>();
anim.mSortedNodes = new ArrayList<Node>();
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.
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index e5e2f18..49e8b9d 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -27,6 +27,7 @@
import android.graphics.Outline;
import android.graphics.PorterDuff;
import android.graphics.Rect;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
@@ -131,7 +132,9 @@
private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
- private final AnimatedVectorDrawableState mAnimatedVectorState;
+ private AnimatedVectorDrawableState mAnimatedVectorState;
+
+ private boolean mMutated;
public AnimatedVectorDrawable() {
mAnimatedVectorState = new AnimatedVectorDrawableState(
@@ -140,7 +143,6 @@
private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res,
Theme theme) {
- // TODO: Correctly handle the constant state for AVD.
mAnimatedVectorState = new AnimatedVectorDrawableState(state);
if (theme != null && canApplyTheme()) {
applyTheme(theme);
@@ -148,8 +150,17 @@
}
@Override
+ public Drawable mutate() {
+ if (!mMutated && super.mutate() == this) {
+ mAnimatedVectorState = new AnimatedVectorDrawableState(mAnimatedVectorState);
+ mMutated = true;
+ }
+ return this;
+ }
+
+ @Override
public ConstantState getConstantState() {
- return null;
+ return mAnimatedVectorState;
}
@Override
@@ -311,14 +322,31 @@
int mChangingConfigurations;
VectorDrawable mVectorDrawable;
ArrayList<Animator> mAnimators;
+ ArrayMap<Animator, String> mTargetNameMap;
public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy) {
if (copy != null) {
mChangingConfigurations = copy.mChangingConfigurations;
- // TODO: Make sure the constant state are handled correctly.
- mVectorDrawable = new VectorDrawable();
- mVectorDrawable.setAllowCaching(false);
- mAnimators = new ArrayList<Animator>();
+ if (copy.mVectorDrawable != null) {
+ mVectorDrawable = (VectorDrawable) copy.mVectorDrawable.getConstantState().newDrawable();
+ mVectorDrawable.mutate();
+ mVectorDrawable.setAllowCaching(false);
+ mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds());
+ }
+ if (copy.mAnimators != null) {
+ final int numAnimators = copy.mAnimators.size();
+ mAnimators = new ArrayList<Animator>(numAnimators);
+ mTargetNameMap = new ArrayMap<Animator, String>(numAnimators);
+ for (int i = 0; i < numAnimators; ++i) {
+ Animator anim = copy.mAnimators.get(i);
+ Animator animClone = anim.clone();
+ String targetName = copy.mTargetNameMap.get(anim);
+ Object targetObject = mVectorDrawable.getTargetByName(targetName);
+ animClone.setTarget(targetObject);
+ mAnimators.add(animClone);
+ mTargetNameMap.put(animClone, targetName);
+ }
+ }
}
}
@@ -346,7 +374,12 @@
private void setupAnimatorsForTarget(String name, Animator animator) {
Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name);
animator.setTarget(target);
+ if (mAnimatedVectorState.mAnimators == null) {
+ mAnimatedVectorState.mAnimators = new ArrayList<Animator>();
+ mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>();
+ }
mAnimatedVectorState.mAnimators.add(animator);
+ mAnimatedVectorState.mTargetNameMap.put(animator, name);
if (DBG_ANIMATION_VECTOR_DRAWABLE) {
Log.v(LOGTAG, "add animator for target " + name + " " + animator);
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index a07ccc4..65ab454 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -629,6 +629,15 @@
mThemeAttrs = copy.mThemeAttrs;
mChangingConfigurations = copy.mChangingConfigurations;
mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
+ if (copy.mVPathRenderer.mFillPaint != null) {
+ mVPathRenderer.mFillPaint = new Paint(copy.mVPathRenderer.mFillPaint);
+ }
+ if (copy.mVPathRenderer.mStrokePaint != null) {
+ mVPathRenderer.mStrokePaint = new Paint(copy.mVPathRenderer.mStrokePaint);
+ }
+ if (copy.mVPathRenderer.mColorFilter != null) {
+ mVPathRenderer.mColorFilter = copy.mVPathRenderer.mColorFilter;
+ }
mTint = copy.mTint;
mTintMode = copy.mTintMode;
mAutoMirrored = copy.mAutoMirrored;
@@ -700,8 +709,8 @@
*/
// Variables that only used temporarily inside the draw() call, so there
// is no need for deep copying.
- private final Path mPath = new Path();
- private final Path mRenderPath = new Path();
+ private final Path mPath;
+ private final Path mRenderPath;
private static final Matrix IDENTITY_MATRIX = new Matrix();
private final Matrix mFinalPathMatrix = new Matrix();
@@ -724,6 +733,8 @@
public VPathRenderer() {
mRootGroup = new VGroup();
+ mPath = new Path();
+ mRenderPath = new Path();
}
public void setRootAlpha(int alpha) {
@@ -736,6 +747,8 @@
public VPathRenderer(VPathRenderer copy) {
mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
+ mPath = new Path(copy.mPath);
+ mRenderPath = new Path(copy.mRenderPath);
mBaseWidth = copy.mBaseWidth;
mBaseHeight = copy.mBaseHeight;
mViewportWidth = copy.mViewportWidth;