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
This commit is contained in:
@@ -23,6 +23,7 @@ import android.graphics.Insets;
|
||||
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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
| mDrawableContainerState.mChangingConfigurations
|
||||
| mDrawableContainerState.mChildrenChangingConfigurations;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean getPadding(Rect padding) {
|
||||
final Rect r = mDrawableContainerState.getConstantPadding();
|
||||
@@ -137,7 +138,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Change the global fade duration when a new drawable is entering
|
||||
* the scene.
|
||||
@@ -146,7 +147,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
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 class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
public void setExitFadeDuration(int ms) {
|
||||
mDrawableContainerState.mExitFadeDuration = ms;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
if (mLastDrawable != null) {
|
||||
@@ -165,12 +166,12 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
mCurrDrawable.setBounds(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isStateful() {
|
||||
return mDrawableContainerState.isStateful();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void jumpToCurrentState() {
|
||||
boolean changed = false;
|
||||
@@ -233,7 +234,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
}
|
||||
return mCurrDrawable != null ? mCurrDrawable.getIntrinsicHeight() : -1;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getMinimumWidth() {
|
||||
if (mDrawableContainerState.isConstantSize()) {
|
||||
@@ -316,7 +317,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
}
|
||||
|
||||
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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void animate(boolean schedule) {
|
||||
final long now = SystemClock.uptimeMillis();
|
||||
boolean animating = false;
|
||||
@@ -432,90 +433,105 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
*/
|
||||
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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
mDrawables[pos] = dr;
|
||||
mNumChildren++;
|
||||
mChildrenChangingConfigurations |= dr.getChangingConfigurations();
|
||||
mHaveStateful = false;
|
||||
mCheckedStateful = false;
|
||||
mCheckedOpacity = false;
|
||||
|
||||
mConstantPadding = null;
|
||||
mPaddingChecked = false;
|
||||
@@ -547,6 +564,18 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
* @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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
if (t.bottom > r.bottom) r.bottom = t.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
mPaddingChecked = true;
|
||||
return (mConstantPadding = r);
|
||||
}
|
||||
@@ -646,12 +719,14 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
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 class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
}
|
||||
|
||||
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 class DrawableContainer extends Drawable implements Drawable.Callback {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user