Postpone creating new drawables for DrawableContainerState.
When creating a DrawableContainerState from a constant state, calls to
ConstantState.newDrawable() are postponed and made as they are needed.
Bug: 9280861
Change-Id: I03c93a43ee00aca3ff618d66d7f507f1722538d1
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index e75ec75..f9cd4e2 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -23,6 +23,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemClock;
+import android.util.SparseArray;
/**
* A helper class that contains several {@link Drawable}s and selects which one to use.
@@ -76,7 +77,7 @@
| mDrawableContainerState.mChangingConfigurations
| mDrawableContainerState.mChildrenChangingConfigurations;
}
-
+
@Override
public boolean getPadding(Rect padding) {
final Rect r = mDrawableContainerState.getConstantPadding();
@@ -137,7 +138,7 @@
}
}
}
-
+
/**
* Change the global fade duration when a new drawable is entering
* the scene.
@@ -146,7 +147,7 @@
public void setEnterFadeDuration(int ms) {
mDrawableContainerState.mEnterFadeDuration = ms;
}
-
+
/**
* Change the global fade duration when a new drawable is leaving
* the scene.
@@ -155,7 +156,7 @@
public void setExitFadeDuration(int ms) {
mDrawableContainerState.mExitFadeDuration = ms;
}
-
+
@Override
protected void onBoundsChange(Rect bounds) {
if (mLastDrawable != null) {
@@ -165,12 +166,12 @@
mCurrDrawable.setBounds(bounds);
}
}
-
+
@Override
public boolean isStateful() {
return mDrawableContainerState.isStateful();
}
-
+
@Override
public void jumpToCurrentState() {
boolean changed = false;
@@ -233,7 +234,7 @@
}
return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1;
}
-
+
@Override
public int getMinimumWidth() {
if (mDrawableContainerState.isConstantSize()) {
@@ -316,7 +317,7 @@
}
if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
- Drawable d = mDrawableContainerState.mDrawables[idx];
+ final Drawable d = mDrawableContainerState.getChild(idx);
mCurrDrawable = d;
mCurIndex = idx;
if (d != null) {
@@ -358,7 +359,7 @@
return true;
}
-
+
void animate(boolean schedule) {
final long now = SystemClock.uptimeMillis();
boolean animating = false;
@@ -432,90 +433,105 @@
*/
public abstract static class DrawableContainerState extends ConstantState {
final DrawableContainer mOwner;
+ final Resources mRes;
- int mChangingConfigurations;
- int mChildrenChangingConfigurations;
-
- Drawable[] mDrawables;
- int mNumChildren;
+ SparseArray<ConstantStateFuture> mDrawableFutures;
- boolean mVariablePadding = false;
- Rect mConstantPadding = null;
+ int mChangingConfigurations;
+ int mChildrenChangingConfigurations;
- boolean mConstantSize = false;
- boolean mComputedConstantSize = false;
- int mConstantWidth;
- int mConstantHeight;
- int mConstantMinimumWidth;
- int mConstantMinimumHeight;
+ Drawable[] mDrawables;
+ int mNumChildren;
- int mOpacity;
+ boolean mVariablePadding;
+ boolean mPaddingChecked;
+ Rect mConstantPadding;
- boolean mHaveStateful = false;
- boolean mStateful;
+ boolean mConstantSize;
+ boolean mComputedConstantSize;
+ int mConstantWidth;
+ int mConstantHeight;
+ int mConstantMinimumWidth;
+ int mConstantMinimumHeight;
- boolean mCheckedConstantState;
- boolean mCanConstantState;
+ boolean mCheckedOpacity;
+ int mOpacity;
- boolean mPaddingChecked = false;
-
- boolean mDither = DEFAULT_DITHER;
+ boolean mCheckedStateful;
+ boolean mStateful;
- int mEnterFadeDuration;
- int mExitFadeDuration;
+ boolean mCheckedConstantState;
+ boolean mCanConstantState;
+
+ boolean mDither = DEFAULT_DITHER;
+
+ boolean mMutated;
+ int mLayoutDirection;
+
+ int mEnterFadeDuration;
+ int mExitFadeDuration;
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
+ mRes = res;
if (orig != null) {
mChangingConfigurations = orig.mChangingConfigurations;
mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
-
- final Drawable[] origDr = orig.mDrawables;
- mDrawables = new Drawable[origDr.length];
- mNumChildren = orig.mNumChildren;
+ mCheckedConstantState = true;
+ mCanConstantState = true;
- final int N = mNumChildren;
- for (int i=0; i<N; i++) {
- if (res != null) {
- mDrawables[i] = origDr[i].getConstantState().newDrawable(res);
- } else {
- mDrawables[i] = origDr[i].getConstantState().newDrawable();
- }
- mDrawables[i].setCallback(owner);
- mDrawables[i].setLayoutDirection(origDr[i].getLayoutDirection());
- }
-
- mCheckedConstantState = mCanConstantState = true;
mVariablePadding = orig.mVariablePadding;
- if (orig.mConstantPadding != null) {
- mConstantPadding = new Rect(orig.mConstantPadding);
- }
mConstantSize = orig.mConstantSize;
- mComputedConstantSize = orig.mComputedConstantSize;
- mConstantWidth = orig.mConstantWidth;
- mConstantHeight = orig.mConstantHeight;
- mConstantMinimumWidth = orig.mConstantMinimumWidth;
- mConstantMinimumHeight = orig.mConstantMinimumHeight;
-
- mOpacity = orig.mOpacity;
- mHaveStateful = orig.mHaveStateful;
- mStateful = orig.mStateful;
-
mDither = orig.mDither;
-
+ mMutated = orig.mMutated;
+ mLayoutDirection = orig.mLayoutDirection;
mEnterFadeDuration = orig.mEnterFadeDuration;
mExitFadeDuration = orig.mExitFadeDuration;
+ // Cloning the following values may require creating futures.
+ mConstantPadding = orig.getConstantPadding();
+ mPaddingChecked = true;
+
+ mConstantWidth = orig.getConstantWidth();
+ mConstantHeight = orig.getConstantHeight();
+ mConstantMinimumWidth = orig.getConstantMinimumWidth();
+ mConstantMinimumHeight = orig.getConstantMinimumHeight();
+ mComputedConstantSize = true;
+
+ mOpacity = orig.getOpacity();
+ mCheckedOpacity = true;
+
+ mStateful = orig.isStateful();
+ mCheckedStateful = true;
+
+ // Postpone cloning children and futures until we're absolutely
+ // sure that we're done computing values for the original state.
+ final Drawable[] origDr = orig.mDrawables;
+ mDrawables = new Drawable[origDr.length];
+ mNumChildren = orig.mNumChildren;
+
+ final SparseArray<ConstantStateFuture> origDf = orig.mDrawableFutures;
+ if (origDf != null) {
+ mDrawableFutures = origDf.clone();
+ } else {
+ mDrawableFutures = new SparseArray<ConstantStateFuture>(mNumChildren);
+ }
+
+ final int N = mNumChildren;
+ for (int i = 0; i < N; i++) {
+ if (origDr[i] != null) {
+ mDrawableFutures.put(i, new ConstantStateFuture(origDr[i]));
+ }
+ }
} else {
mDrawables = new Drawable[10];
mNumChildren = 0;
- mCheckedConstantState = mCanConstantState = false;
}
}
-
+
@Override
public int getChangingConfigurations() {
return mChangingConfigurations | mChildrenChangingConfigurations;
@@ -534,7 +550,8 @@
mDrawables[pos] = dr;
mNumChildren++;
mChildrenChangingConfigurations |= dr.getChangingConfigurations();
- mHaveStateful = false;
+ mCheckedStateful = false;
+ mCheckedOpacity = false;
mConstantPadding = null;
mPaddingChecked = false;
@@ -547,6 +564,18 @@
return mDrawables.length;
}
+ private final void createAllFutures() {
+ if (mDrawableFutures != null) {
+ final int futureCount = mDrawableFutures.size();
+ for (int keyIndex = 0; keyIndex < futureCount; keyIndex++) {
+ final int index = mDrawableFutures.keyAt(keyIndex);
+ mDrawables[index] = mDrawableFutures.valueAt(keyIndex).get(this);
+ }
+
+ mDrawableFutures = null;
+ }
+ }
+
public final int getChildCount() {
return mNumChildren;
}
@@ -555,25 +584,65 @@
* @deprecated Use {@link #getChild} instead.
*/
public final Drawable[] getChildren() {
+ // Create all futures for backwards compatibility.
+ createAllFutures();
+
return mDrawables;
}
public final Drawable getChild(int index) {
- return mDrawables[index];
+ final Drawable result = mDrawables[index];
+ if (result != null) {
+ return result;
+ }
+
+ // Prepare future drawable if necessary.
+ if (mDrawableFutures != null) {
+ final int keyIndex = mDrawableFutures.indexOfKey(index);
+ if (keyIndex >= 0) {
+ final Drawable prepared = mDrawableFutures.valueAt(keyIndex).get(this);
+ mDrawables[index] = prepared;
+ mDrawableFutures.removeAt(keyIndex);
+ return prepared;
+ }
+ }
+
+ return null;
+ }
+
+ final void setLayoutDirection(int layoutDirection) {
+ // No need to call createAllFutures, since future drawables will
+ // change layout direction when they are prepared.
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ if (drawables[i] != null) {
+ drawables[i].setLayoutDirection(layoutDirection);
+ }
+ }
+
+ mLayoutDirection = layoutDirection;
}
final void mutate() {
- final int N = getChildCount();
+ // No need to call createAllFutures, since future drawables will
+ // mutate when they are prepared.
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
- if (drawables[i] != null) drawables[i].mutate();
+ if (drawables[i] != null) {
+ drawables[i].mutate();
+ }
}
+
+ mMutated = true;
}
- /** A boolean value indicating whether to use the maximum padding value of
- * all frames in the set (false), or to use the padding value of the frame
- * being shown (true). Default value is false.
- */
+ /**
+ * A boolean value indicating whether to use the maximum padding value
+ * of all frames in the set (false), or to use the padding value of the
+ * frame being shown (true). Default value is false.
+ */
public final void setVariablePadding(boolean variable) {
mVariablePadding = variable;
}
@@ -582,13 +651,16 @@
if (mVariablePadding) {
return null;
}
- if (mConstantPadding != null || mPaddingChecked) {
+
+ if ((mConstantPadding != null) || mPaddingChecked) {
return mConstantPadding;
}
+ createAllFutures();
+
Rect r = null;
final Rect t = new Rect();
- final int N = getChildCount();
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
if (drawables[i].getPadding(t)) {
@@ -599,6 +671,7 @@
if (t.bottom > r.bottom) r.bottom = t.bottom;
}
}
+
mPaddingChecked = true;
return (mConstantPadding = r);
}
@@ -646,12 +719,14 @@
protected void computeConstantSize() {
mComputedConstantSize = true;
- final int N = getChildCount();
+ createAllFutures();
+
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
mConstantWidth = mConstantHeight = -1;
mConstantMinimumWidth = mConstantMinimumHeight = 0;
for (int i = 0; i < N; i++) {
- Drawable dr = drawables[i];
+ final Drawable dr = drawables[i];
int s = dr.getIntrinsicWidth();
if (s > mConstantWidth) mConstantWidth = s;
s = dr.getIntrinsicHeight();
@@ -680,33 +755,45 @@
}
public final int getOpacity() {
- final int N = getChildCount();
+ if (mCheckedOpacity) {
+ return mOpacity;
+ }
+
+ createAllFutures();
+
+ mCheckedOpacity = true;
+
+ final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
- int op = N > 0 ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
+ int op = (N > 0) ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
for (int i = 1; i < N; i++) {
op = Drawable.resolveOpacity(op, drawables[i].getOpacity());
}
+
mOpacity = op;
return op;
}
public final boolean isStateful() {
- if (mHaveStateful) {
+ if (mCheckedStateful) {
return mStateful;
}
-
- boolean stateful = false;
- final int N = getChildCount();
+
+ createAllFutures();
+
+ mCheckedStateful = true;
+
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
- if (mDrawables[i].isStateful()) {
- stateful = true;
- break;
+ if (drawables[i].isStateful()) {
+ mStateful = true;
+ return true;
}
}
-
- mStateful = stateful;
- mHaveStateful = true;
- return stateful;
+
+ mStateful = false;
+ return false;
}
public void growArray(int oldSize, int newSize) {
@@ -716,24 +803,60 @@
}
public synchronized boolean canConstantState() {
- if (!mCheckedConstantState) {
- mCanConstantState = true;
- final int N = mNumChildren;
- for (int i=0; i<N; i++) {
- if (mDrawables[i].getConstantState() == null) {
- mCanConstantState = false;
- break;
- }
- }
- mCheckedConstantState = true;
+ if (mCheckedConstantState) {
+ return mCanConstantState;
}
- return mCanConstantState;
+ createAllFutures();
+
+ mCheckedConstantState = true;
+
+ final int N = mNumChildren;
+ final Drawable[] drawables = mDrawables;
+ for (int i = 0; i < N; i++) {
+ if (drawables[i].getConstantState() == null) {
+ mCanConstantState = false;
+ return false;
+ }
+ }
+
+ mCanConstantState = true;
+ return true;
+ }
+
+ /**
+ * Class capable of cloning a Drawable from another Drawable's
+ * ConstantState.
+ */
+ private static class ConstantStateFuture {
+ private final ConstantState mConstantState;
+
+ private ConstantStateFuture(Drawable source) {
+ mConstantState = source.getConstantState();
+ }
+
+ /**
+ * Obtains and prepares the Drawable represented by this future.
+ *
+ * @param state the container into which this future will be placed
+ * @return a prepared Drawable
+ */
+ public Drawable get(DrawableContainerState state) {
+ final Drawable result = (state.mRes == null) ?
+ mConstantState.newDrawable() : mConstantState.newDrawable(state.mRes);
+ result.setLayoutDirection(state.mLayoutDirection);
+ result.setCallback(state.mOwner);
+
+ if (state.mMutated) {
+ result.mutate();
+ }
+
+ return result;
+ }
}
}
- protected void setConstantState(DrawableContainerState state)
- {
+ protected void setConstantState(DrawableContainerState state) {
mDrawableContainerState = state;
}
}
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 5ecd296..e3c7bc5 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -264,11 +264,11 @@
/** @hide */
@Override
public void setLayoutDirection(int layoutDirection) {
- final int numStates = getStateCount();
- for (int i = 0; i < numStates; i++) {
- getStateDrawable(i).setLayoutDirection(layoutDirection);
- }
super.setLayoutDirection(layoutDirection);
+
+ // Let the container handle setting its own layout direction. Otherwise,
+ // we're accessing potentially unused states.
+ mStateListState.setLayoutDirection(layoutDirection);
}
static final class StateListState extends DrawableContainerState {