Merge "Unify wrapper-type drawables"

This commit is contained in:
Alan Viverette
2015-02-23 20:38:58 +00:00
committed by Android (Google) Code Review
14 changed files with 1028 additions and 1622 deletions

View File

@@ -11797,15 +11797,8 @@ package android.graphics.drawable {
method public final void setTileModeY(android.graphics.Shader.TileMode);
}
public class ClipDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
public class ClipDrawable extends android.graphics.drawable.DrawableWrapper {
ctor public ClipDrawable(android.graphics.drawable.Drawable, int, int);
method public void draw(android.graphics.Canvas);
method public int getOpacity();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
field public static final int HORIZONTAL = 1; // 0x1
field public static final int VERTICAL = 2; // 0x2
}
@@ -11847,8 +11840,10 @@ package android.graphics.drawable {
method public android.graphics.Rect getDirtyBounds();
method public boolean getDither();
method public boolean getFilterBitmap();
method public void getHotspotBounds(android.graphics.Rect);
method public int getIntrinsicHeight();
method public int getIntrinsicWidth();
method public int getLayoutDirection();
method public final int getLevel();
method public int getMinimumHeight();
method public int getMinimumWidth();
@@ -11866,6 +11861,7 @@ package android.graphics.drawable {
method public void jumpToCurrentState();
method public android.graphics.drawable.Drawable mutate();
method protected void onBoundsChange(android.graphics.Rect);
method public boolean onLayoutDirectionChange(int);
method protected boolean onLevelChange(int);
method protected boolean onStateChange(int[]);
method public static int resolveOpacity(int, int);
@@ -11882,6 +11878,7 @@ package android.graphics.drawable {
method public void setFilterBitmap(boolean);
method public void setHotspot(float, float);
method public void setHotspotBounds(int, int, int, int);
method public final boolean setLayoutDirection(int);
method public final boolean setLevel(int);
method public boolean setState(int[]);
method public void setTint(int);
@@ -11946,6 +11943,19 @@ package android.graphics.drawable {
method public final void setVariablePadding(boolean);
}
public abstract class DrawableWrapper extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
ctor public DrawableWrapper(android.graphics.drawable.Drawable);
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable getDrawable();
method public int getOpacity();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void setDrawable(android.graphics.drawable.Drawable);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
}
public class GradientDrawable extends android.graphics.drawable.Drawable {
ctor public GradientDrawable();
ctor public GradientDrawable(android.graphics.drawable.GradientDrawable.Orientation, int[]);
@@ -11993,17 +12003,9 @@ package android.graphics.drawable {
enum_constant public static final android.graphics.drawable.GradientDrawable.Orientation TR_BL;
}
public class InsetDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
public class InsetDrawable extends android.graphics.drawable.DrawableWrapper {
ctor public InsetDrawable(android.graphics.drawable.Drawable, int);
ctor public InsetDrawable(android.graphics.drawable.Drawable, int, int, int, int);
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable getDrawable();
method public int getOpacity();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
}
public class LayerDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -12083,41 +12085,24 @@ package android.graphics.drawable {
field public static final int RADIUS_AUTO = -1; // 0xffffffff
}
public class RotateDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
public class RotateDrawable extends android.graphics.drawable.DrawableWrapper {
ctor public RotateDrawable();
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable getDrawable();
method public float getFromDegrees();
method public int getOpacity();
method public float getPivotX();
method public float getPivotY();
method public float getToDegrees();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public boolean isPivotXRelative();
method public boolean isPivotYRelative();
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void setDrawable(android.graphics.drawable.Drawable);
method public void setFromDegrees(float);
method public void setPivotX(float);
method public void setPivotXRelative(boolean);
method public void setPivotY(float);
method public void setPivotYRelative(boolean);
method public void setToDegrees(float);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
}
public class ScaleDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
public class ScaleDrawable extends android.graphics.drawable.DrawableWrapper {
ctor public ScaleDrawable(android.graphics.drawable.Drawable, int, float, float);
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable getDrawable();
method public int getOpacity();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
}
public class ShapeDrawable extends android.graphics.drawable.Drawable {

View File

@@ -12071,15 +12071,8 @@ package android.graphics.drawable {
method public final void setTileModeY(android.graphics.Shader.TileMode);
}
public class ClipDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
public class ClipDrawable extends android.graphics.drawable.DrawableWrapper {
ctor public ClipDrawable(android.graphics.drawable.Drawable, int, int);
method public void draw(android.graphics.Canvas);
method public int getOpacity();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
field public static final int HORIZONTAL = 1; // 0x1
field public static final int VERTICAL = 2; // 0x2
}
@@ -12121,8 +12114,10 @@ package android.graphics.drawable {
method public android.graphics.Rect getDirtyBounds();
method public boolean getDither();
method public boolean getFilterBitmap();
method public void getHotspotBounds(android.graphics.Rect);
method public int getIntrinsicHeight();
method public int getIntrinsicWidth();
method public int getLayoutDirection();
method public final int getLevel();
method public int getMinimumHeight();
method public int getMinimumWidth();
@@ -12140,6 +12135,7 @@ package android.graphics.drawable {
method public void jumpToCurrentState();
method public android.graphics.drawable.Drawable mutate();
method protected void onBoundsChange(android.graphics.Rect);
method public boolean onLayoutDirectionChange(int);
method protected boolean onLevelChange(int);
method protected boolean onStateChange(int[]);
method public static int resolveOpacity(int, int);
@@ -12156,6 +12152,7 @@ package android.graphics.drawable {
method public void setFilterBitmap(boolean);
method public void setHotspot(float, float);
method public void setHotspotBounds(int, int, int, int);
method public final boolean setLayoutDirection(int);
method public final boolean setLevel(int);
method public boolean setState(int[]);
method public void setTint(int);
@@ -12220,6 +12217,19 @@ package android.graphics.drawable {
method public final void setVariablePadding(boolean);
}
public abstract class DrawableWrapper extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
ctor public DrawableWrapper(android.graphics.drawable.Drawable);
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable getDrawable();
method public int getOpacity();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void setDrawable(android.graphics.drawable.Drawable);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
}
public class GradientDrawable extends android.graphics.drawable.Drawable {
ctor public GradientDrawable();
ctor public GradientDrawable(android.graphics.drawable.GradientDrawable.Orientation, int[]);
@@ -12267,17 +12277,9 @@ package android.graphics.drawable {
enum_constant public static final android.graphics.drawable.GradientDrawable.Orientation TR_BL;
}
public class InsetDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
public class InsetDrawable extends android.graphics.drawable.DrawableWrapper {
ctor public InsetDrawable(android.graphics.drawable.Drawable, int);
ctor public InsetDrawable(android.graphics.drawable.Drawable, int, int, int, int);
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable getDrawable();
method public int getOpacity();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
}
public class LayerDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -12357,41 +12359,24 @@ package android.graphics.drawable {
field public static final int RADIUS_AUTO = -1; // 0xffffffff
}
public class RotateDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
public class RotateDrawable extends android.graphics.drawable.DrawableWrapper {
ctor public RotateDrawable();
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable getDrawable();
method public float getFromDegrees();
method public int getOpacity();
method public float getPivotX();
method public float getPivotY();
method public float getToDegrees();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public boolean isPivotXRelative();
method public boolean isPivotYRelative();
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void setDrawable(android.graphics.drawable.Drawable);
method public void setFromDegrees(float);
method public void setPivotX(float);
method public void setPivotXRelative(boolean);
method public void setPivotY(float);
method public void setPivotYRelative(boolean);
method public void setToDegrees(float);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
}
public class ScaleDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
public class ScaleDrawable extends android.graphics.drawable.DrawableWrapper {
ctor public ScaleDrawable(android.graphics.drawable.Drawable, int, float, float);
method public void draw(android.graphics.Canvas);
method public android.graphics.drawable.Drawable getDrawable();
method public int getOpacity();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
}
public class ShapeDrawable extends android.graphics.drawable.Drawable {

View File

@@ -20,15 +20,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff.Mode;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.util.Log;
import android.os.SystemClock;
import org.xmlpull.v1.XmlPullParser;
@@ -41,35 +37,15 @@ import com.android.internal.R;
/**
* @hide
*/
public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable,
Animatable {
private static final String TAG = "AnimatedRotateDrawable";
public class AnimatedRotateDrawable extends DrawableWrapper implements Runnable, Animatable {
private AnimatedRotateState mState;
private boolean mMutated;
private float mCurrentDegrees;
private float mIncrement;
private boolean mRunning;
public AnimatedRotateDrawable() {
this(null, null);
}
private AnimatedRotateDrawable(AnimatedRotateState rotateState, Resources res) {
mState = new AnimatedRotateState(rotateState, this, res);
init();
}
private void init() {
final AnimatedRotateState state = mState;
mIncrement = 360.0f / state.mFramesCount;
final Drawable drawable = state.mDrawable;
if (drawable != null) {
drawable.setFilterBitmap(true);
if (drawable instanceof BitmapDrawable) {
((BitmapDrawable) drawable).setAntiAlias(true);
}
}
this(new AnimatedRotateState(null), null);
}
@Override
@@ -131,8 +107,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
@Override
public boolean setVisible(boolean visible, boolean restart) {
mState.mDrawable.setVisible(visible, restart);
boolean changed = super.setVisible(visible, restart);
final boolean changed = super.setVisible(visible, restart);
if (visible) {
if (changed || restart) {
mCurrentDegrees = 0.0f;
@@ -144,123 +119,6 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
return changed;
}
/**
* Returns the drawable rotated by this RotateDrawable.
*/
public Drawable getDrawable() {
return mState.mDrawable;
}
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations()
| mState.mChangingConfigurations
| mState.mDrawable.getChangingConfigurations();
}
@Override
public void setAlpha(int alpha) {
mState.mDrawable.setAlpha(alpha);
}
@Override
public int getAlpha() {
return mState.mDrawable.getAlpha();
}
@Override
public void setColorFilter(ColorFilter cf) {
mState.mDrawable.setColorFilter(cf);
}
@Override
public void setTintList(ColorStateList tint) {
mState.mDrawable.setTintList(tint);
}
@Override
public void setTintMode(Mode tintMode) {
mState.mDrawable.setTintMode(tintMode);
}
@Override
public int getOpacity() {
return mState.mDrawable.getOpacity();
}
@Override
public boolean canApplyTheme() {
return (mState != null && mState.canApplyTheme()) || super.canApplyTheme();
}
@Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
}
}
@Override
public boolean getPadding(Rect padding) {
return mState.mDrawable.getPadding(padding);
}
@Override
public boolean isStateful() {
return mState.mDrawable.isStateful();
}
@Override
protected void onBoundsChange(Rect bounds) {
mState.mDrawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
}
@Override
protected boolean onLevelChange(int level) {
return mState.mDrawable.setLevel(level);
}
@Override
protected boolean onStateChange(int[] state) {
return mState.mDrawable.setState(state);
}
@Override
public int getIntrinsicWidth() {
return mState.mDrawable.getIntrinsicWidth();
}
@Override
public int getIntrinsicHeight() {
return mState.mDrawable.getIntrinsicHeight();
}
@Override
public ConstantState getConstantState() {
if (mState.canConstantState()) {
mState.mChangingConfigurations = getChangingConfigurations();
return mState;
}
return null;
}
@Override
public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
@@ -268,50 +126,27 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimatedRotateDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
a.recycle();
inflateChildElements(r, parser, attrs, theme);
updateLocalState();
}
init();
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
throw new XmlPullParserException(a.getPositionDescription()
+ ": <animated-rotate> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
}
@Override
public void applyTheme(@Nullable Theme t) {
super.applyTheme(t);
void updateStateFromTypedArray(TypedArray a) {
super.updateStateFromTypedArray(a);
final AnimatedRotateState state = mState;
if (state == null) {
return;
}
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(
state.mThemeAttrs, R.styleable.AnimatedRotateDrawable);
try {
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
}
}
if (state.mDrawable != null && state.mDrawable.canApplyTheme()) {
state.mDrawable.applyTheme(t);
}
init();
}
private void updateStateFromTypedArray(TypedArray a) {
final AnimatedRotateState state = mState;
// Account for any configuration changes.
state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotX)) {
final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
@@ -332,43 +167,35 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
final Drawable dr = a.getDrawable(R.styleable.AnimatedRotateDrawable_drawable);
if (dr != null) {
state.mDrawable = dr;
dr.setCallback(this);
setDrawable(dr);
}
}
private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
@Override
public void applyTheme(@Nullable Theme t) {
final AnimatedRotateState state = mState;
if (state == null) {
return;
}
Drawable dr = null;
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
if ((dr = Drawable.createFromXmlInner(r, parser, attrs, theme)) == null) {
Log.w(TAG, "Bad element under <animated-rotate>: " + parser.getName());
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(
state.mThemeAttrs, R.styleable.AnimatedRotateDrawable);
try {
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
}
}
if (dr != null) {
state.mDrawable = dr;
dr.setCallback(this);
}
}
// The drawable may have changed as a result of applying the theme, so
// apply the theme to the wrapped drawable last.
super.applyTheme(t);
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (mState.mDrawable == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
throw new XmlPullParserException(a.getPositionDescription()
+ ": <animated-rotate> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
updateLocalState();
}
public void setFramesCount(int framesCount) {
@@ -380,25 +207,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
mState.mFrameDuration = framesDuration;
}
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mState.mDrawable.mutate();
mMutated = true;
}
return this;
}
/**
* @hide
*/
public void clearMutated() {
super.clearMutated();
mState.mDrawable.clearMutated();
mMutated = false;
}
final static class AnimatedRotateState extends Drawable.ConstantState {
static final class AnimatedRotateState extends DrawableWrapper.DrawableWrapperState {
Drawable mDrawable;
int[] mThemeAttrs;
@@ -414,34 +223,19 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
private boolean mCanConstantState;
private boolean mCheckedConstantState;
public AnimatedRotateState(AnimatedRotateState orig, AnimatedRotateDrawable owner,
Resources res) {
public AnimatedRotateState(AnimatedRotateState orig) {
super(orig);
if (orig != null) {
if (res != null) {
mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
} else {
mDrawable = orig.mDrawable.getConstantState().newDrawable();
}
mDrawable.setCallback(owner);
mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection());
mDrawable.setBounds(orig.mDrawable.getBounds());
mDrawable.setLevel(orig.mDrawable.getLevel());
mThemeAttrs = orig.mThemeAttrs;
mPivotXRel = orig.mPivotXRel;
mPivotX = orig.mPivotX;
mPivotYRel = orig.mPivotYRel;
mPivotY = orig.mPivotY;
mFramesCount = orig.mFramesCount;
mFrameDuration = orig.mFrameDuration;
mCanConstantState = mCheckedConstantState = true;
}
}
@Override
public Drawable newDrawable() {
return new AnimatedRotateDrawable(this, null);
}
@Override
public Drawable newDrawable(Resources res) {
return new AnimatedRotateDrawable(this, res);
@@ -467,4 +261,27 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
return mCanConstantState;
}
}
private AnimatedRotateDrawable(AnimatedRotateState state, Resources res) {
super(state, res);
mState = state;
updateLocalState();
}
private void updateLocalState() {
final AnimatedRotateState state = mState;
mIncrement = 360.0f / state.mFramesCount;
// Force the wrapped drawable to use filtering and AA, if applicable,
// so that it looks smooth when rotated.
final Drawable drawable = state.mDrawable;
if (drawable != null) {
drawable.setFilterBitmap(true);
if (drawable instanceof BitmapDrawable) {
((BitmapDrawable) drawable).setAntiAlias(true);
}
}
}
}

View File

@@ -196,6 +196,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable {
return mAnimatedVectorState.mVectorDrawable.setLevel(level);
}
@Override
public boolean onLayoutDirectionChange(int layoutDirection) {
return mAnimatedVectorState.mVectorDrawable.setLayoutDirection(layoutDirection);
}
@Override
public int getAlpha() {
return mAnimatedVectorState.mVectorDrawable.getAlpha();
@@ -237,12 +242,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable {
return super.setVisible(visible, restart);
}
/** {@hide} */
@Override
public void setLayoutDirection(int layoutDirection) {
mAnimatedVectorState.mVectorDrawable.setLayoutDirection(layoutDirection);
}
@Override
public boolean isStateful() {
return mAnimatedVectorState.mVectorDrawable.isStateful();

View File

@@ -50,32 +50,36 @@ import java.io.IOException;
* @attr ref android.R.styleable#ClipDrawable_gravity
* @attr ref android.R.styleable#ClipDrawable_drawable
*/
public class ClipDrawable extends Drawable implements Drawable.Callback {
private ClipState mState;
private final Rect mTmpRect = new Rect();
public class ClipDrawable extends DrawableWrapper {
public static final int HORIZONTAL = 1;
public static final int VERTICAL = 2;
private boolean mMutated;
private static final int MAX_LEVEL = 10000;
private final Rect mTmpRect = new Rect();
private ClipState mState;
ClipDrawable() {
this(null, null);
this(new ClipState(null), null);
}
/**
* @param orientation Bitwise-or of {@link #HORIZONTAL} and/or {@link #VERTICAL}
* Creates a new clip drawable with the specified gravity and orientation.
*
* @param drawable the drawable to clip
* @param gravity gravity constant (see {@link Gravity} used to position
* the clipped drawable within the parent container
* @param orientation bitwise-or of {@link #HORIZONTAL} and/or
* {@link #VERTICAL}
*/
public ClipDrawable(Drawable drawable, int gravity, int orientation) {
this(null, null);
this(new ClipState(null), null);
mState.mDrawable = drawable;
mState.mGravity = gravity;
mState.mOrientation = orientation;
if (drawable != null) {
drawable.setCallback(this);
}
setDrawable(drawable);
}
@Override
@@ -84,39 +88,15 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
super.inflate(r, parser, attrs, theme);
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable);
// Reset mDrawable to preserve old multiple-inflate behavior. This is
// silly, but we have CTS tests that rely on it.
mState.mDrawable = null;
updateStateFromTypedArray(a);
inflateChildElements(r, parser, attrs, theme);
inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
Drawable dr = null;
int type;
final int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
if (dr != null) {
mState.mDrawable = dr;
dr.setCallback(this);
}
}
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (mState.mDrawable == null && (mState.mThemeAttrs == null
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) {
throw new XmlPullParserException(a.getPositionDescription()
+ ": <clip> tag requires a 'drawable' attribute or "
@@ -124,292 +104,110 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
}
}
private void updateStateFromTypedArray(TypedArray a) {
@Override
void updateStateFromTypedArray(TypedArray a) {
super.updateStateFromTypedArray(a);
final ClipState state = mState;
// Account for any configuration changes.
state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
state.mOrientation = a.getInt(R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
state.mGravity = a.getInt(R.styleable.ClipDrawable_gravity, state.mGravity);
state.mOrientation = a.getInt(
R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
state.mGravity = a.getInt(
R.styleable.ClipDrawable_gravity, state.mGravity);
final Drawable dr = a.getDrawable(R.styleable.ClipDrawable_drawable);
if (dr != null) {
state.mDrawable = dr;
dr.setCallback(this);
setDrawable(dr);
}
}
@Override
public void applyTheme(Theme t) {
super.applyTheme(t);
final ClipState state = mState;
if (state == null || state.mThemeAttrs == null) {
if (state == null) {
return;
}
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ClipDrawable);
try {
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ClipDrawable);
try {
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
}
}
if (state.mDrawable != null && state.mDrawable.canApplyTheme()) {
state.mDrawable.applyTheme(t);
}
}
@Override
public boolean canApplyTheme() {
return (mState != null && mState.canApplyTheme()) || super.canApplyTheme();
}
// overrides from Drawable.Callback
@Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
}
}
// overrides from Drawable
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations()
| mState.mChangingConfigurations
| mState.mDrawable.getChangingConfigurations();
}
@Override
public boolean getPadding(Rect padding) {
// XXX need to adjust padding!
return mState.mDrawable.getPadding(padding);
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
mState.mDrawable.setVisible(visible, restart);
return super.setVisible(visible, restart);
}
@Override
public void setAlpha(int alpha) {
mState.mDrawable.setAlpha(alpha);
}
@Override
public int getAlpha() {
return mState.mDrawable.getAlpha();
}
@Override
public void setColorFilter(ColorFilter cf) {
mState.mDrawable.setColorFilter(cf);
}
@Override
public void setTintList(ColorStateList tint) {
mState.mDrawable.setTintList(tint);
}
@Override
public void setTintMode(Mode tintMode) {
mState.mDrawable.setTintMode(tintMode);
}
@Override
public int getOpacity() {
return mState.mDrawable.getOpacity();
}
@Override
public boolean isStateful() {
return mState.mDrawable.isStateful();
}
@Override
protected boolean onStateChange(int[] state) {
return mState.mDrawable.setState(state);
// The drawable may have changed as a result of applying the theme, so
// apply the theme to the wrapped drawable last.
super.applyTheme(t);
}
@Override
protected boolean onLevelChange(int level) {
mState.mDrawable.setLevel(level);
super.onLevelChange(level);
invalidateSelf();
return true;
}
@Override
protected void onBoundsChange(Rect bounds) {
mState.mDrawable.setBounds(bounds);
}
@Override
public void draw(Canvas canvas) {
if (mState.mDrawable.getLevel() == 0) {
final Drawable dr = getDrawable();
if (dr.getLevel() == 0) {
return;
}
final Rect r = mTmpRect;
final Rect bounds = getBounds();
int level = getLevel();
final int level = getLevel();
int w = bounds.width();
final int iw = 0; //mState.mDrawable.getIntrinsicWidth();
if ((mState.mOrientation & HORIZONTAL) != 0) {
w -= (w - iw) * (10000 - level) / 10000;
w -= (w - iw) * (MAX_LEVEL - level) / MAX_LEVEL;
}
int h = bounds.height();
final int ih = 0; //mState.mDrawable.getIntrinsicHeight();
if ((mState.mOrientation & VERTICAL) != 0) {
h -= (h - ih) * (10000 - level) / 10000;
h -= (h - ih) * (MAX_LEVEL - level) / MAX_LEVEL;
}
final int layoutDirection = getLayoutDirection();
Gravity.apply(mState.mGravity, w, h, bounds, r, layoutDirection);
if (w > 0 && h > 0) {
canvas.save();
canvas.clipRect(r);
mState.mDrawable.draw(canvas);
dr.draw(canvas);
canvas.restore();
}
}
@Override
public int getIntrinsicWidth() {
return mState.mDrawable.getIntrinsicWidth();
}
@Override
public int getIntrinsicHeight() {
return mState.mDrawable.getIntrinsicHeight();
}
@Override
public ConstantState getConstantState() {
if (mState.canConstantState()) {
mState.mChangingConfigurations = getChangingConfigurations();
return mState;
}
return null;
}
/** @hide */
@Override
public void setLayoutDirection(int layoutDirection) {
mState.mDrawable.setLayoutDirection(layoutDirection);
super.setLayoutDirection(layoutDirection);
}
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mState.mDrawable.mutate();
mMutated = true;
}
return this;
}
/**
* @hide
*/
public void clearMutated() {
super.clearMutated();
mState.mDrawable.clearMutated();
mMutated = false;
}
final static class ClipState extends ConstantState {
int[] mThemeAttrs;
int mChangingConfigurations;
Drawable mDrawable;
static final class ClipState extends DrawableWrapper.DrawableWrapperState {
int mOrientation = HORIZONTAL;
int mGravity = Gravity.LEFT;
private boolean mCheckedConstantState;
private boolean mCanConstantState;
ClipState(ClipState orig) {
super(orig);
ClipState(ClipState orig, ClipDrawable owner, Resources res) {
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
if (res != null) {
mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
} else {
mDrawable = orig.mDrawable.getConstantState().newDrawable();
}
mDrawable.setCallback(owner);
mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection());
mDrawable.setBounds(orig.mDrawable.getBounds());
mDrawable.setLevel(orig.mDrawable.getLevel());
mOrientation = orig.mOrientation;
mGravity = orig.mGravity;
mCheckedConstantState = mCanConstantState = true;
}
}
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme())
|| super.canApplyTheme();
}
@Override
public Drawable newDrawable() {
return new ClipDrawable(this, null);
}
@Override
public Drawable newDrawable(Resources res) {
return new ClipDrawable(this, res);
}
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
}
boolean canConstantState() {
if (!mCheckedConstantState) {
mCanConstantState = mDrawable.getConstantState() != null;
mCheckedConstantState = true;
}
return mCanConstantState;
}
}
private ClipDrawable(ClipState state, Resources res) {
mState = new ClipState(state, this, res);
super(state, res);
mState = state;
}
}

View File

@@ -423,27 +423,41 @@ public abstract class Drawable {
* Returns the resolved layout direction for this Drawable.
*
* @return One of {@link android.view.View#LAYOUT_DIRECTION_LTR},
* {@link android.view.View#LAYOUT_DIRECTION_RTL}
*
* @hide
* {@link android.view.View#LAYOUT_DIRECTION_RTL}
* @see #setLayoutDirection(int)
*/
public int getLayoutDirection() {
return mLayoutDirection;
}
/**
* Set the layout direction for this drawable. Should be a resolved direction as the
* Drawable as no capacity to do the resolution on his own.
* Set the layout direction for this drawable. Should be a resolved
* layout direction, as the Drawable as no capacity to do the resolution on
* its own.
*
* @param layoutDirection One of {@link android.view.View#LAYOUT_DIRECTION_LTR},
* {@link android.view.View#LAYOUT_DIRECTION_RTL}
*
* @hide
* @param layoutDirection the resolved layout direction for the drawable,
* either {@link android.view.View#LAYOUT_DIRECTION_LTR}
* or {@link android.view.View#LAYOUT_DIRECTION_RTL}
* @see #getLayoutDirection()
*/
public void setLayoutDirection(@View.ResolvedLayoutDir int layoutDirection) {
if (getLayoutDirection() != layoutDirection) {
public final boolean setLayoutDirection(@View.ResolvedLayoutDir int layoutDirection) {
if (mLayoutDirection != layoutDirection) {
mLayoutDirection = layoutDirection;
return onLayoutDirectionChange(layoutDirection);
}
return false;
}
/**
* Called when the drawable's resolved layout direction changes.
*
* @param layoutDirection the new resolved layout direction
* @return true if the layout direction change has caused the appearance of
* the drawable to change and it needs to be re-drawn
* @see #setLayoutDirection(int)
*/
public boolean onLayoutDirectionChange(@View.ResolvedLayoutDir int layoutDirection) {
return false;
}
/**
@@ -554,14 +568,20 @@ public abstract class Drawable {
* Sets the bounds to which the hotspot is constrained, if they should be
* different from the drawable bounds.
*
* @param left
* @param top
* @param right
* @param bottom
* @param left position in pixels of the left bound
* @param top position in pixels of the top bound
* @param right position in pixels of the right bound
* @param bottom position in pixels of the bottom bound
* @see #getHotspotBounds(android.graphics.Rect)
*/
public void setHotspotBounds(int left, int top, int right, int bottom) {}
/** @hide For internal use only. Individual results may vary. */
/**
* Populates {@code outRect} with the hotspot bounds.
*
* @param outRect the rect to populate with the hotspot bounds
* @see #setHotspotBounds(int, int, int, int)
*/
public void getHotspotBounds(Rect outRect) {
outRect.set(getBounds());
}

View File

@@ -306,7 +306,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
}
/** @hide */
@Override
public void getHotspotBounds(Rect outRect) {
if (mHotspotBounds != null) {
@@ -338,6 +337,13 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return false;
}
@Override
public boolean onLayoutDirectionChange(int layoutDirection) {
// Let the container handle setting its own layout direction. Otherwise,
// we're accessing potentially unused states.
return mDrawableContainerState.setLayoutDirection(layoutDirection, getCurrentIndex());
}
@Override
public int getIntrinsicWidth() {
if (mDrawableContainerState.isConstantSize()) {
@@ -829,18 +835,25 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
return null;
}
final void setLayoutDirection(int layoutDirection) {
final boolean setLayoutDirection(int layoutDirection, int currentIndex) {
boolean changed = false;
// 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);
final boolean childChanged = drawables[i].setLayoutDirection(layoutDirection);
if (i == currentIndex) {
changed = childChanged;
}
}
}
mLayoutDirection = layoutDirection;
return changed;
}
final void applyTheme(Theme theme) {

View File

@@ -0,0 +1,443 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics.drawable;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import java.io.IOException;
import java.util.Collection;
/**
* Drawable container with only one child element.
*/
public abstract class DrawableWrapper extends Drawable implements Drawable.Callback {
private DrawableWrapperState mState;
private Drawable mDrawable;
private boolean mMutated;
DrawableWrapper(DrawableWrapperState state, Resources res) {
mState = state;
updateLocalState(res);
}
/**
* Creates a new wrapper around the specified drawable.
*
* @param dr the drawable to wrap
*/
public DrawableWrapper(@Nullable Drawable dr) {
mState = null;
mDrawable = dr;
}
/**
* Initializes local dynamic properties from state. This should be called
* after significant state changes, e.g. from the One True Constructor and
* after inflating or applying a theme.
*/
private void updateLocalState(Resources res) {
if (mState != null && mState.mDrawableState != null) {
final Drawable dr = mState.mDrawableState.newDrawable(res);
setDrawable(dr);
}
}
/**
* Sets the wrapped drawable.
*
* @param dr the wrapped drawable
*/
public void setDrawable(@Nullable Drawable dr) {
if (mDrawable != null) {
mDrawable.setCallback(null);
}
mDrawable = dr;
if (dr != null) {
dr.setCallback(this);
// Only call setters for data that's stored in the base Drawable.
dr.setVisible(isVisible(), true);
dr.setState(getState());
dr.setLevel(getLevel());
dr.setBounds(getBounds());
dr.setLayoutDirection(getLayoutDirection());
if (mState != null) {
mState.mDrawableState = dr.getConstantState();
}
}
invalidateSelf();
}
/**
* @return the wrapped drawable
*/
@Nullable
public Drawable getDrawable() {
return mDrawable;
}
void updateStateFromTypedArray(TypedArray a) {
final DrawableWrapperState state = mState;
if (state == null) {
return;
}
// Account for any configuration changes.
state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
// TODO: Consider using R.styleable.DrawableWrapper_drawable
}
@Override
public void applyTheme(Resources.Theme t) {
super.applyTheme(t);
final DrawableWrapperState state = mState;
if (state == null) {
return;
}
if (mDrawable != null && mDrawable.canApplyTheme()) {
mDrawable.applyTheme(t);
}
}
@Override
public boolean canApplyTheme() {
return (mState != null && mState.canApplyTheme()) || super.canApplyTheme();
}
@Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
}
}
@Override
public void draw(@NonNull Canvas canvas) {
if (mDrawable != null) {
mDrawable.draw(canvas);
}
}
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations()
| (mState != null ? mState.mChangingConfigurations : 0)
| mDrawable.getChangingConfigurations();
}
@Override
public boolean getPadding(@NonNull Rect padding) {
return mDrawable != null && mDrawable.getPadding(padding);
}
/** @hide */
@Override
public Insets getOpticalInsets() {
return mDrawable != null ? mDrawable.getOpticalInsets() : Insets.NONE;
}
@Override
public void setHotspot(float x, float y) {
if (mDrawable != null) {
mDrawable.setHotspot(x, y);
}
}
@Override
public void setHotspotBounds(int left, int top, int right, int bottom) {
if (mDrawable != null) {
mDrawable.setHotspotBounds(left, top, right, bottom);
}
}
@Override
public void getHotspotBounds(@NonNull Rect outRect) {
if (mDrawable != null) {
mDrawable.getHotspotBounds(outRect);
} else {
outRect.set(getBounds());
}
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
final boolean superChanged = super.setVisible(visible, restart);
final boolean changed = mDrawable != null && mDrawable.setVisible(visible, restart);
return superChanged | changed;
}
@Override
public void setAlpha(int alpha) {
if (mDrawable != null) {
mDrawable.setAlpha(alpha);
}
}
@Override
public int getAlpha() {
return mDrawable != null ? mDrawable.getAlpha() : 255;
}
@Override
public void setColorFilter(@Nullable ColorFilter cf) {
if (mDrawable != null) {
mDrawable.setColorFilter(cf);
}
}
@Override
public void setTintList(@Nullable ColorStateList tint) {
if (mDrawable != null) {
mDrawable.setTintList(tint);
}
}
@Override
public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
if (mDrawable != null) {
mDrawable.setTintMode(tintMode);
}
}
@Override
public boolean onLayoutDirectionChange(@View.ResolvedLayoutDir int layoutDirection) {
return mDrawable != null && mDrawable.setLayoutDirection(layoutDirection);
}
@Override
public int getOpacity() {
return mDrawable != null ? mDrawable.getOpacity() : PixelFormat.TRANSPARENT;
}
@Override
public boolean isStateful() {
return mDrawable != null && mDrawable.isStateful();
}
@Override
protected boolean onStateChange(int[] state) {
if (mDrawable != null) {
final boolean changed = mDrawable.setState(state);
if (changed) {
onBoundsChange(getBounds());
}
return changed;
}
return false;
}
@Override
protected boolean onLevelChange(int level) {
return mDrawable != null && mDrawable.setLevel(level);
}
@Override
protected void onBoundsChange(@NonNull Rect bounds) {
if (mDrawable != null) {
mDrawable.setBounds(bounds);
}
}
@Override
public int getIntrinsicWidth() {
return mDrawable != null ? mDrawable.getIntrinsicWidth() : -1;
}
@Override
public int getIntrinsicHeight() {
return mDrawable != null ? mDrawable.getIntrinsicHeight() : -1;
}
@Override
public void getOutline(@NonNull Outline outline) {
if (mDrawable != null) {
mDrawable.getOutline(outline);
} else {
super.getOutline(outline);
}
}
@Override
@Nullable
public ConstantState getConstantState() {
if (mState != null && mState.canConstantState()) {
mState.mChangingConfigurations = getChangingConfigurations();
return mState;
}
return null;
}
@Override
@NonNull
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mState = mutateConstantState();
if (mDrawable != null) {
mDrawable.mutate();
}
if (mState != null) {
mState.mDrawableState = mDrawable != null ? mDrawable.getConstantState() : null;
}
mMutated = true;
}
return this;
}
/**
* Mutates the constant state and returns the new state. Responsible for
* updating any local copy.
* <p>
* This method should never call the super implementation; it should always
* mutate and return its own constant state.
*
* @return the new state
*/
DrawableWrapperState mutateConstantState() {
return mState;
}
/**
* @hide Only used by the framework for pre-loading resources.
*/
public void clearMutated() {
super.clearMutated();
if (mDrawable != null) {
mDrawable.clearMutated();
}
mMutated = false;
}
/**
* Called during inflation to inflate the child element.
*/
void inflateChildDrawable(Resources r, XmlPullParser parser, AttributeSet attrs,
Resources.Theme theme) throws XmlPullParserException, IOException {
// Drawable specified on the root element takes precedence.
if (getDrawable() != null) {
return;
}
// Seek to the first child element.
Drawable dr = null;
int type;
final int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.START_TAG) {
dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
break;
}
}
if (dr != null) {
setDrawable(dr);
}
}
abstract static class DrawableWrapperState extends Drawable.ConstantState {
int[] mThemeAttrs;
int mChangingConfigurations;
Drawable.ConstantState mDrawableState;
DrawableWrapperState(DrawableWrapperState orig) {
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
mDrawableState = orig.mDrawableState;
}
}
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null
|| (mDrawableState != null && mDrawableState.canApplyTheme())
|| super.canApplyTheme();
}
@Override
public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
final Drawable.ConstantState state = mDrawableState;
if (state != null) {
return state.addAtlasableBitmaps(atlasList);
}
return 0;
}
@Override
public Drawable newDrawable() {
return newDrawable(null);
}
@Override
public abstract Drawable newDrawable(Resources res);
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
}
public boolean canConstantState() {
return mDrawableState != null;
}
}
}

View File

@@ -18,27 +18,20 @@ package android.graphics.drawable;
import com.android.internal.R;
import android.annotation.NonNull;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.res.ColorStateList;
import android.annotation.NonNull;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.content.res.TypedArray;
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.util.AttributeSet;
import java.io.IOException;
import java.util.Collection;
/**
* A Drawable that insets another Drawable by a specified distance.
@@ -56,13 +49,10 @@ import java.util.Collection;
* @attr ref android.R.styleable#InsetDrawable_insetTop
* @attr ref android.R.styleable#InsetDrawable_insetBottom
*/
public class InsetDrawable extends Drawable implements Drawable.Callback {
public class InsetDrawable extends DrawableWrapper {
private final Rect mTmpRect = new Rect();
private InsetState mState;
private Drawable mDrawable;
private boolean mMutated;
/**
* No-arg constructor used by drawable inflation.
@@ -94,72 +84,29 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
int insetBottom) {
this(new InsetState(), null);
mState.mDrawableState = drawable == null ? null : drawable.getConstantState();
mState.mInsetLeft = insetLeft;
mState.mInsetTop = insetTop;
mState.mInsetRight = insetRight;
mState.mInsetBottom = insetBottom;
mDrawable = drawable;
if (drawable != null) {
drawable.setCallback(this);
}
setDrawable(drawable);
}
@Override
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.InsetDrawable_visible);
super.inflate(r, parser, attrs, theme);
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
updateStateFromTypedArray(a);
inflateChildElements(r, parser, attrs, theme);
inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
private void setDrawable(Drawable dr) {
if (mDrawable != null) {
mDrawable.setCallback(null);
}
mDrawable = dr;
if (dr != null) {
dr.setCallback(this);
dr.setVisible(isVisible(), true);
dr.setState(getState());
dr.setLevel(getLevel());
dr.setBounds(getBounds());
dr.setLayoutDirection(getLayoutDirection());
}
}
private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
// Load inner XML elements.
if (mDrawable == null) {
int type;
while ((type=parser.next()) == XmlPullParser.TEXT) {
}
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException(parser.getPositionDescription()
+ ": <inset> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
final Drawable dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
if (dr != null) {
mState.mDrawableState = dr.getConstantState();
setDrawable(dr);
}
}
}
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (mDrawable == null && (mState.mThemeAttrs == null
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
throw new XmlPullParserException(a.getPositionDescription()
+ ": <inset> tag requires a 'drawable' attribute or "
@@ -167,15 +114,11 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
}
}
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
@Override
void updateStateFromTypedArray(TypedArray a) {
super.updateStateFromTypedArray(a);
final InsetState state = mState;
// Account for any configuration changes.
state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
final int attr = a.getIndex(i);
@@ -183,7 +126,6 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
case R.styleable.InsetDrawable_drawable:
final Drawable dr = a.getDrawable(attr);
if (dr != null) {
mState.mDrawableState = dr.getConstantState();
setDrawable(dr);
}
break;
@@ -214,8 +156,6 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
@Override
public void applyTheme(Theme t) {
super.applyTheme(t);
final InsetState state = mState;
if (state == null) {
return;
@@ -233,55 +173,14 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
}
}
if (mDrawable != null && mDrawable.canApplyTheme()) {
mDrawable.applyTheme(t);
}
}
@Override
public boolean canApplyTheme() {
return (mState != null && mState.canApplyTheme()) || super.canApplyTheme();
}
@Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
}
}
@Override
public void draw(Canvas canvas) {
mDrawable.draw(canvas);
}
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations()
| mState.mChangingConfigurations
| mDrawable.getChangingConfigurations();
// The drawable may have changed as a result of applying the theme, so
// apply the theme to the wrapped drawable last.
super.applyTheme(t);
}
@Override
public boolean getPadding(Rect padding) {
final boolean pad = mDrawable.getPadding(padding);
final boolean pad = super.getPadding(padding);
padding.left += mState.mInsetLeft;
padding.right += mState.mInsetRight;
@@ -302,63 +201,10 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
contentInsets.bottom + mState.mInsetBottom);
}
@Override
public void setHotspot(float x, float y) {
mDrawable.setHotspot(x, y);
}
@Override
public void setHotspotBounds(int left, int top, int right, int bottom) {
mDrawable.setHotspotBounds(left, top, right, bottom);
}
/** @hide */
@Override
public void getHotspotBounds(Rect outRect) {
mDrawable.getHotspotBounds(outRect);
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
mDrawable.setVisible(visible, restart);
return super.setVisible(visible, restart);
}
@Override
public void setAlpha(int alpha) {
mDrawable.setAlpha(alpha);
}
@Override
public int getAlpha() {
return mDrawable.getAlpha();
}
@Override
public void setColorFilter(ColorFilter cf) {
mDrawable.setColorFilter(cf);
}
@Override
public void setTintList(ColorStateList tint) {
mDrawable.setTintList(tint);
}
@Override
public void setTintMode(Mode tintMode) {
mDrawable.setTintMode(tintMode);
}
/** {@hide} */
@Override
public void setLayoutDirection(int layoutDirection) {
mDrawable.setLayoutDirection(layoutDirection);
}
@Override
public int getOpacity() {
final InsetState state = mState;
final int opacity = mDrawable.getOpacity();
final int opacity = getDrawable().getOpacity();
if (opacity == PixelFormat.OPAQUE && (state.mInsetLeft > 0 || state.mInsetTop > 0
|| state.mInsetRight > 0 || state.mInsetBottom > 0)) {
return PixelFormat.TRANSLUCENT;
@@ -366,23 +212,6 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
return opacity;
}
@Override
public boolean isStateful() {
return mDrawable.isStateful();
}
@Override
protected boolean onStateChange(int[] state) {
final boolean changed = mDrawable.setState(state);
onBoundsChange(getBounds());
return changed;
}
@Override
protected boolean onLevelChange(int level) {
return mDrawable.setLevel(level);
}
@Override
protected void onBoundsChange(Rect bounds) {
final Rect r = mTmpRect;
@@ -393,22 +222,23 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
r.right -= mState.mInsetRight;
r.bottom -= mState.mInsetBottom;
mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
// Apply inset bounds to the wrapped drawable.
super.onBoundsChange(r);
}
@Override
public int getIntrinsicWidth() {
return mDrawable.getIntrinsicWidth() + mState.mInsetLeft + mState.mInsetRight;
return getDrawable().getIntrinsicWidth() + mState.mInsetLeft + mState.mInsetRight;
}
@Override
public int getIntrinsicHeight() {
return mDrawable.getIntrinsicHeight() + mState.mInsetTop + mState.mInsetBottom;
return getDrawable().getIntrinsicHeight() + mState.mInsetTop + mState.mInsetBottom;
}
@Override
public void getOutline(@NonNull Outline outline) {
mDrawable.getOutline(outline);
getDrawable().getOutline(outline);
}
@Override
@@ -421,33 +251,12 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
}
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mState = new InsetState(mState);
mDrawable.mutate();
mState.mDrawableState = mDrawable.getConstantState();
mMutated = true;
}
return this;
DrawableWrapperState mutateConstantState() {
mState = new InsetState(mState);
return mState;
}
/**
* @hide
*/
public void clearMutated() {
super.clearMutated();
mDrawable.clearMutated();
mMutated = false;
}
/**
* Returns the drawable wrapped by this InsetDrawable. May be null.
*/
public Drawable getDrawable() {
return mDrawable;
}
private static final class InsetState extends ConstantState {
static final class InsetState extends DrawableWrapper.DrawableWrapperState {
int[] mThemeAttrs;
int mChangingConfigurations;
@@ -458,15 +267,14 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
int mInsetRight = 0;
int mInsetBottom = 0;
public InsetState() {
// Empty constructor.
InsetState() {
this(null);
}
public InsetState(InsetState orig) {
InsetState(InsetState orig) {
super(orig);
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
mDrawableState = orig.mDrawableState;
mInsetLeft = orig.mInsetLeft;
mInsetTop = orig.mInsetTop;
mInsetRight = orig.mInsetRight;
@@ -474,40 +282,10 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
}
}
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null
|| (mDrawableState != null && mDrawableState.canApplyTheme())
|| super.canApplyTheme();
}
@Override
public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
final ConstantState state = mDrawableState;
if (state != null) {
return state.addAtlasableBitmaps(atlasList);
}
return 0;
}
@Override
public Drawable newDrawable() {
return new InsetDrawable(this, null);
}
@Override
public Drawable newDrawable(Resources res) {
return new InsetDrawable(this, res);
}
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
}
public boolean canConstantState() {
return mDrawableState != null;
}
}
/**
@@ -515,21 +293,9 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
* constructors to set the state and initialize local properties.
*/
private InsetDrawable(InsetState state, Resources res) {
super(state, res);
mState = state;
updateLocalState(res);
}
/**
* Initializes local dynamic properties from state. This should be called
* after significant state changes, e.g. from the One True Constructor and
* after inflating or applying a theme.
*/
private void updateLocalState(Resources res) {
if (mState.mDrawableState != null) {
final Drawable dr = mState.mDrawableState.newDrawable(res);
setDrawable(dr);
}
}
}

View File

@@ -834,7 +834,6 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
}
/** @hide */
@Override
public void getHotspotBounds(Rect outRect) {
if (mHotspotBounds != null) {
@@ -1230,16 +1229,16 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
mMutated = false;
}
/** @hide */
@Override
public void setLayoutDirection(int layoutDirection) {
super.setLayoutDirection(layoutDirection);
public boolean onLayoutDirectionChange(int layoutDirection) {
boolean changed = false;
final ChildDrawable[] array = mLayerState.mChildren;
final int N = mLayerState.mNum;
for (int i = 0; i < N; i++) {
array[i].mDrawable.setLayoutDirection(layoutDirection);
changed |= array[i].mDrawable.setLayoutDirection(layoutDirection);
}
updateLayerBounds(getBounds());
return changed;
}
static class ChildDrawable {

View File

@@ -614,7 +614,6 @@ public class RippleDrawable extends LayerDrawable {
onHotspotBoundsChanged();
}
/** @hide */
@Override
public void getHotspotBounds(Rect outRect) {
outRect.set(mHotspotBounds);

View File

@@ -29,6 +29,7 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.util.MathUtils;
import android.util.TypedValue;
import android.util.AttributeSet;
@@ -51,12 +52,10 @@ import java.io.IOException;
* @attr ref android.R.styleable#RotateDrawable_pivotY
* @attr ref android.R.styleable#RotateDrawable_drawable
*/
public class RotateDrawable extends Drawable implements Drawable.Callback {
private static final float MAX_LEVEL = 10000.0f;
public class RotateDrawable extends DrawableWrapper {
private static final int MAX_LEVEL = 10000;
private final RotateState mState;
private boolean mMutated;
private RotateState mState;
/**
* Create a new rotating drawable with an empty state.
@@ -65,416 +64,32 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
this(null, null);
}
/**
* Create a new rotating drawable with the specified state. A copy of
* this state is used as the internal state for the newly created
* drawable.
*
* @param rotateState the state for this drawable
*/
private RotateDrawable(RotateState rotateState, Resources res) {
mState = new RotateState(rotateState, this, res);
}
@Override
public void draw(Canvas canvas) {
final RotateState st = mState;
final Drawable d = st.mDrawable;
final Rect bounds = d.getBounds();
final int w = bounds.right - bounds.left;
final int h = bounds.bottom - bounds.top;
final float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX;
final float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY;
final int saveCount = canvas.save();
canvas.rotate(st.mCurrentDegrees, px + bounds.left, py + bounds.top);
d.draw(canvas);
canvas.restoreToCount(saveCount);
}
/**
* Sets the drawable rotated by this RotateDrawable.
*
* @param drawable The drawable to rotate
*/
public void setDrawable(Drawable drawable) {
final Drawable oldDrawable = mState.mDrawable;
if (oldDrawable != drawable) {
if (oldDrawable != null) {
oldDrawable.setCallback(null);
}
mState.mDrawable = drawable;
if (drawable != null) {
drawable.setCallback(this);
}
}
}
/**
* @return The drawable rotated by this RotateDrawable
*/
public Drawable getDrawable() {
return mState.mDrawable;
}
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations()
| mState.mChangingConfigurations
| mState.mDrawable.getChangingConfigurations();
}
@Override
public void setAlpha(int alpha) {
mState.mDrawable.setAlpha(alpha);
}
@Override
public int getAlpha() {
return mState.mDrawable.getAlpha();
}
@Override
public void setColorFilter(ColorFilter cf) {
mState.mDrawable.setColorFilter(cf);
}
@Override
public void setTintList(ColorStateList tint) {
mState.mDrawable.setTintList(tint);
}
@Override
public void setTintMode(Mode tintMode) {
mState.mDrawable.setTintMode(tintMode);
}
@Override
public int getOpacity() {
return mState.mDrawable.getOpacity();
}
/**
* Sets the start angle for rotation.
*
* @param fromDegrees Starting angle in degrees
*
* @see #getFromDegrees()
* @attr ref android.R.styleable#RotateDrawable_fromDegrees
*/
public void setFromDegrees(float fromDegrees) {
if (mState.mFromDegrees != fromDegrees) {
mState.mFromDegrees = fromDegrees;
invalidateSelf();
}
}
/**
* @return The starting angle for rotation in degrees
*
* @see #setFromDegrees(float)
* @attr ref android.R.styleable#RotateDrawable_fromDegrees
*/
public float getFromDegrees() {
return mState.mFromDegrees;
}
/**
* Sets the end angle for rotation.
*
* @param toDegrees Ending angle in degrees
*
* @see #getToDegrees()
* @attr ref android.R.styleable#RotateDrawable_toDegrees
*/
public void setToDegrees(float toDegrees) {
if (mState.mToDegrees != toDegrees) {
mState.mToDegrees = toDegrees;
invalidateSelf();
}
}
/**
* @return The ending angle for rotation in degrees
*
* @see #setToDegrees(float)
* @attr ref android.R.styleable#RotateDrawable_toDegrees
*/
public float getToDegrees() {
return mState.mToDegrees;
}
/**
* Sets the X position around which the drawable is rotated.
*
* @param pivotX X position around which to rotate. If the X pivot is
* relative, the position represents a fraction of the drawable
* width. Otherwise, the position represents an absolute value in
* pixels.
*
* @see #setPivotXRelative(boolean)
* @attr ref android.R.styleable#RotateDrawable_pivotX
*/
public void setPivotX(float pivotX) {
if (mState.mPivotX != pivotX) {
mState.mPivotX = pivotX;
invalidateSelf();
}
}
/**
* @return X position around which to rotate
*
* @see #setPivotX(float)
* @attr ref android.R.styleable#RotateDrawable_pivotX
*/
public float getPivotX() {
return mState.mPivotX;
}
/**
* Sets whether the X pivot value represents a fraction of the drawable
* width or an absolute value in pixels.
*
* @param relative True if the X pivot represents a fraction of the drawable
* width, or false if it represents an absolute value in pixels
*
* @see #isPivotXRelative()
*/
public void setPivotXRelative(boolean relative) {
if (mState.mPivotXRel != relative) {
mState.mPivotXRel = relative;
invalidateSelf();
}
}
/**
* @return True if the X pivot represents a fraction of the drawable width,
* or false if it represents an absolute value in pixels
*
* @see #setPivotXRelative(boolean)
*/
public boolean isPivotXRelative() {
return mState.mPivotXRel;
}
/**
* Sets the Y position around which the drawable is rotated.
*
* @param pivotY Y position around which to rotate. If the Y pivot is
* relative, the position represents a fraction of the drawable
* height. Otherwise, the position represents an absolute value
* in pixels.
*
* @see #getPivotY()
* @attr ref android.R.styleable#RotateDrawable_pivotY
*/
public void setPivotY(float pivotY) {
if (mState.mPivotY != pivotY) {
mState.mPivotY = pivotY;
invalidateSelf();
}
}
/**
* @return Y position around which to rotate
*
* @see #setPivotY(float)
* @attr ref android.R.styleable#RotateDrawable_pivotY
*/
public float getPivotY() {
return mState.mPivotY;
}
/**
* Sets whether the Y pivot value represents a fraction of the drawable
* height or an absolute value in pixels.
*
* @param relative True if the Y pivot represents a fraction of the drawable
* height, or false if it represents an absolute value in pixels
*
* @see #isPivotYRelative()
*/
public void setPivotYRelative(boolean relative) {
if (mState.mPivotYRel != relative) {
mState.mPivotYRel = relative;
invalidateSelf();
}
}
/**
* @return True if the Y pivot represents a fraction of the drawable height,
* or false if it represents an absolute value in pixels
*
* @see #setPivotYRelative(boolean)
*/
public boolean isPivotYRelative() {
return mState.mPivotYRel;
}
@Override
public boolean canApplyTheme() {
return (mState != null && mState.canApplyTheme()) || super.canApplyTheme();
}
@Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
}
}
@Override
public boolean getPadding(Rect padding) {
return mState.mDrawable.getPadding(padding);
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
mState.mDrawable.setVisible(visible, restart);
return super.setVisible(visible, restart);
}
@Override
public boolean isStateful() {
return mState.mDrawable.isStateful();
}
@Override
protected boolean onStateChange(int[] state) {
final boolean changed = mState.mDrawable.setState(state);
onBoundsChange(getBounds());
return changed;
}
@Override
protected boolean onLevelChange(int level) {
mState.mDrawable.setLevel(level);
onBoundsChange(getBounds());
mState.mCurrentDegrees = mState.mFromDegrees +
(mState.mToDegrees - mState.mFromDegrees) *
(level / MAX_LEVEL);
invalidateSelf();
return true;
}
@Override
protected void onBoundsChange(Rect bounds) {
mState.mDrawable.setBounds(bounds.left, bounds.top,
bounds.right, bounds.bottom);
}
@Override
public int getIntrinsicWidth() {
return mState.mDrawable.getIntrinsicWidth();
}
@Override
public int getIntrinsicHeight() {
return mState.mDrawable.getIntrinsicHeight();
}
@Override
public ConstantState getConstantState() {
if (mState.canConstantState()) {
mState.mChangingConfigurations = getChangingConfigurations();
return mState;
}
return null;
}
@Override
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RotateDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.RotateDrawable_visible);
// Reset mDrawable to preserve old multiple-inflate behavior. This is
// silly, but we have CTS tests that rely on it.
mState.mDrawable = null;
updateStateFromTypedArray(a);
inflateChildElements(r, parser, attrs, theme);
inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
@Override
public void applyTheme(Theme t) {
super.applyTheme(t);
final RotateState state = mState;
if (state == null) {
return;
}
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.RotateDrawable);
try {
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
}
}
if (state.mDrawable != null && state.mDrawable.canApplyTheme()) {
state.mDrawable.applyTheme(t);
}
}
private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
Drawable dr = null;
int type;
final int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
if (dr != null) {
mState.mDrawable = dr;
dr.setCallback(this);
}
}
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (mState.mDrawable == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.ScaleDrawable_drawable] == 0)) {
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.RotateDrawable_drawable] == 0)) {
throw new XmlPullParserException(a.getPositionDescription()
+ ": <rotate> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
}
private void updateStateFromTypedArray(TypedArray a) {
@Override
void updateStateFromTypedArray(TypedArray a) {
super.updateStateFromTypedArray(a);
final RotateState state = mState;
// Account for any configuration changes.
@@ -495,72 +110,235 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
state.mPivotY = state.mPivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
}
state.mFromDegrees = a.getFloat(R.styleable.RotateDrawable_fromDegrees, state.mFromDegrees);
state.mToDegrees = a.getFloat(R.styleable.RotateDrawable_toDegrees, state.mToDegrees);
state.mFromDegrees = a.getFloat(
R.styleable.RotateDrawable_fromDegrees, state.mFromDegrees);
state.mToDegrees = a.getFloat(
R.styleable.RotateDrawable_toDegrees, state.mToDegrees);
state.mCurrentDegrees = state.mFromDegrees;
final Drawable dr = a.getDrawable(R.styleable.RotateDrawable_drawable);
if (dr != null) {
state.mDrawable = dr;
dr.setCallback(this);
setDrawable(dr);
}
}
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mState.mDrawable.mutate();
mMutated = true;
public void applyTheme(Theme t) {
final RotateState state = mState;
if (state == null) {
return;
}
return this;
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.RotateDrawable);
try {
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
a.recycle();
}
}
// The drawable may have changed as a result of applying the theme, so
// apply the theme to the wrapped drawable last.
super.applyTheme(t);
}
@Override
public void draw(Canvas canvas) {
final Drawable d = getDrawable();
final Rect bounds = d.getBounds();
final int w = bounds.right - bounds.left;
final int h = bounds.bottom - bounds.top;
final RotateState st = mState;
final float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX;
final float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY;
final int saveCount = canvas.save();
canvas.rotate(st.mCurrentDegrees, px + bounds.left, py + bounds.top);
d.draw(canvas);
canvas.restoreToCount(saveCount);
}
/**
* @hide
* Sets the start angle for rotation.
*
* @param fromDegrees starting angle in degrees
* @see #getFromDegrees()
* @attr ref android.R.styleable#RotateDrawable_fromDegrees
*/
public void clearMutated() {
super.clearMutated();
mState.mDrawable.clearMutated();
mMutated = false;
public void setFromDegrees(float fromDegrees) {
if (mState.mFromDegrees != fromDegrees) {
mState.mFromDegrees = fromDegrees;
invalidateSelf();
}
}
/**
* Represents the state of a rotation for a given drawable. The same
* rotate drawable can be invoked with different states to drive several
* rotations at the same time.
* @return starting angle for rotation in degrees
* @see #setFromDegrees(float)
* @attr ref android.R.styleable#RotateDrawable_fromDegrees
*/
final static class RotateState extends Drawable.ConstantState {
int[] mThemeAttrs;
int mChangingConfigurations;
public float getFromDegrees() {
return mState.mFromDegrees;
}
Drawable mDrawable;
/**
* Sets the end angle for rotation.
*
* @param toDegrees ending angle in degrees
* @see #getToDegrees()
* @attr ref android.R.styleable#RotateDrawable_toDegrees
*/
public void setToDegrees(float toDegrees) {
if (mState.mToDegrees != toDegrees) {
mState.mToDegrees = toDegrees;
invalidateSelf();
}
}
/**
* @return ending angle for rotation in degrees
* @see #setToDegrees(float)
* @attr ref android.R.styleable#RotateDrawable_toDegrees
*/
public float getToDegrees() {
return mState.mToDegrees;
}
/**
* Sets the X position around which the drawable is rotated.
*
* @param pivotX X position around which to rotate. If the X pivot is
* relative, the position represents a fraction of the drawable
* width. Otherwise, the position represents an absolute value in
* pixels.
* @see #setPivotXRelative(boolean)
* @attr ref android.R.styleable#RotateDrawable_pivotX
*/
public void setPivotX(float pivotX) {
if (mState.mPivotX != pivotX) {
mState.mPivotX = pivotX;
invalidateSelf();
}
}
/**
* @return X position around which to rotate
* @see #setPivotX(float)
* @attr ref android.R.styleable#RotateDrawable_pivotX
*/
public float getPivotX() {
return mState.mPivotX;
}
/**
* Sets whether the X pivot value represents a fraction of the drawable
* width or an absolute value in pixels.
*
* @param relative true if the X pivot represents a fraction of the drawable
* width, or false if it represents an absolute value in pixels
* @see #isPivotXRelative()
*/
public void setPivotXRelative(boolean relative) {
if (mState.mPivotXRel != relative) {
mState.mPivotXRel = relative;
invalidateSelf();
}
}
/**
* @return true if the X pivot represents a fraction of the drawable width,
* or false if it represents an absolute value in pixels
* @see #setPivotXRelative(boolean)
*/
public boolean isPivotXRelative() {
return mState.mPivotXRel;
}
/**
* Sets the Y position around which the drawable is rotated.
*
* @param pivotY Y position around which to rotate. If the Y pivot is
* relative, the position represents a fraction of the drawable
* height. Otherwise, the position represents an absolute value
* in pixels.
* @see #getPivotY()
* @attr ref android.R.styleable#RotateDrawable_pivotY
*/
public void setPivotY(float pivotY) {
if (mState.mPivotY != pivotY) {
mState.mPivotY = pivotY;
invalidateSelf();
}
}
/**
* @return Y position around which to rotate
* @see #setPivotY(float)
* @attr ref android.R.styleable#RotateDrawable_pivotY
*/
public float getPivotY() {
return mState.mPivotY;
}
/**
* Sets whether the Y pivot value represents a fraction of the drawable
* height or an absolute value in pixels.
*
* @param relative True if the Y pivot represents a fraction of the drawable
* height, or false if it represents an absolute value in pixels
* @see #isPivotYRelative()
*/
public void setPivotYRelative(boolean relative) {
if (mState.mPivotYRel != relative) {
mState.mPivotYRel = relative;
invalidateSelf();
}
}
/**
* @return true if the Y pivot represents a fraction of the drawable height,
* or false if it represents an absolute value in pixels
* @see #setPivotYRelative(boolean)
*/
public boolean isPivotYRelative() {
return mState.mPivotYRel;
}
@Override
protected boolean onLevelChange(int level) {
super.onLevelChange(level);
final float value = level / (float) MAX_LEVEL;
final float degrees = MathUtils.lerp(mState.mFromDegrees, mState.mToDegrees, value);
mState.mCurrentDegrees = degrees;
invalidateSelf();
return true;
}
@Override
DrawableWrapperState mutateConstantState() {
mState = new RotateState(mState);
return mState;
}
static final class RotateState extends DrawableWrapper.DrawableWrapperState {
boolean mPivotXRel = true;
float mPivotX = 0.5f;
boolean mPivotYRel = true;
float mPivotY = 0.5f;
float mFromDegrees = 0.0f;
float mToDegrees = 360.0f;
float mCurrentDegrees = 0.0f;
private boolean mCheckedConstantState;
private boolean mCanConstantState;
RotateState(RotateState orig) {
super(orig);
RotateState(RotateState orig, RotateDrawable owner, Resources res) {
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
if (res != null) {
mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
} else {
mDrawable = orig.mDrawable.getConstantState().newDrawable();
}
mDrawable.setCallback(owner);
mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection());
mDrawable.setBounds(orig.mDrawable.getBounds());
mDrawable.setLevel(orig.mDrawable.getLevel());
mPivotXRel = orig.mPivotXRel;
mPivotX = orig.mPivotX;
mPivotYRel = orig.mPivotYRel;
@@ -568,38 +346,18 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
mFromDegrees = orig.mFromDegrees;
mToDegrees = orig.mToDegrees;
mCurrentDegrees = orig.mCurrentDegrees;
mCheckedConstantState = mCanConstantState = true;
}
}
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme())
|| super.canApplyTheme();
}
@Override
public Drawable newDrawable() {
return new RotateDrawable(this, null);
}
@Override
public Drawable newDrawable(Resources res) {
return new RotateDrawable(this, res);
}
}
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
}
private RotateDrawable(RotateState state, Resources res) {
super(state, res);
public boolean canConstantState() {
if (!mCheckedConstantState) {
mCanConstantState = mDrawable.getConstantState() != null;
mCheckedConstantState = true;
}
return mCanConstantState;
}
mState = state;
}
}

View File

@@ -21,17 +21,17 @@ import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.graphics.*;
import android.graphics.PorterDuff.Mode;
import android.view.Gravity;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import java.io.IOException;
import java.util.Collection;
/**
* A Drawable that changes the size of another Drawable based on its current
@@ -49,44 +49,37 @@ import java.util.Collection;
* @attr ref android.R.styleable#ScaleDrawable_scaleGravity
* @attr ref android.R.styleable#ScaleDrawable_drawable
*/
public class ScaleDrawable extends Drawable implements Drawable.Callback {
private ScaleState mState;
private boolean mMutated;
public class ScaleDrawable extends DrawableWrapper {
private static final int MAX_LEVEL = 10000;
private final Rect mTmpRect = new Rect();
private ScaleState mState;
ScaleDrawable() {
this(null, null);
this(new ScaleState(null), null);
}
/**
* Creates a new scale drawable with the specified gravity and scale
* properties.
*
* @param drawable the drawable to scale
* @param gravity gravity constant (see {@link Gravity} used to position
* the scaled drawable within the parent container
* @param scaleWidth width scaling factor [0...1] to use then the level is
* at the maximum value, or -1 to not scale width
* @param scaleHeight height scaling factor [0...1] to use then the level
* is at the maximum value, or -1 to not scale height
*/
public ScaleDrawable(Drawable drawable, int gravity, float scaleWidth, float scaleHeight) {
this(null, null);
this(new ScaleState(null), null);
mState.mDrawable = drawable;
mState.mGravity = gravity;
mState.mScaleWidth = scaleWidth;
mState.mScaleHeight = scaleHeight;
if (drawable != null) {
drawable.setCallback(this);
}
}
/**
* Returns the drawable scaled by this ScaleDrawable.
*/
public Drawable getDrawable() {
return mState.mDrawable;
}
private static float getPercent(TypedArray a, int name, float defaultValue) {
final String s = a.getString(name);
if (s != null) {
if (s.endsWith("%")) {
String f = s.substring(0, s.length() - 1);
return Float.parseFloat(f) / 100.0f;
}
}
return defaultValue;
setDrawable(drawable);
}
@Override
@@ -95,28 +88,70 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback {
super.inflate(r, parser, attrs, theme);
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ScaleDrawable);
// Reset mDrawable to preserve old multiple-inflate behavior. This is
// silly, but we have CTS tests that rely on it.
mState.mDrawable = null;
updateStateFromTypedArray(a);
inflateChildElements(r, parser, attrs, theme);
inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.ScaleDrawable_drawable] == 0)) {
throw new XmlPullParserException(a.getPositionDescription()
+ ": <scale> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
}
@Override
void updateStateFromTypedArray(TypedArray a) {
super.updateStateFromTypedArray(a);
final ScaleState state = mState;
state.mScaleWidth = getPercent(a,
R.styleable.ScaleDrawable_scaleWidth, state.mScaleWidth);
state.mScaleHeight = getPercent(a,
R.styleable.ScaleDrawable_scaleHeight, state.mScaleHeight);
state.mGravity = a.getInt(
R.styleable.ScaleDrawable_scaleGravity, state.mGravity);
state.mUseIntrinsicSizeAsMin = a.getBoolean(
R.styleable.ScaleDrawable_useIntrinsicSizeAsMinimum, state.mUseIntrinsicSizeAsMin);
final Drawable dr = a.getDrawable(R.styleable.ScaleDrawable_drawable);
if (dr != null) {
setDrawable(dr);
}
}
private static float getPercent(TypedArray a, int index, float defaultValue) {
final int type = a.getType(index);
if (type == TypedValue.TYPE_FRACTION || type == TypedValue.TYPE_NULL) {
return a.getFraction(index, 1, 1, defaultValue);
}
// Coerce to float.
final String s = a.getString(index);
if (s != null) {
if (s.endsWith("%")) {
final String f = s.substring(0, s.length() - 1);
return Float.parseFloat(f) / 100.0f;
}
}
return defaultValue;
}
@Override
public void applyTheme(Theme t) {
super.applyTheme(t);
final ScaleState state = mState;
if (state == null) {
return;
}
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ScaleDrawable);
final TypedArray a = t.resolveAttributes(
state.mThemeAttrs, R.styleable.ScaleDrawable);
try {
updateStateFromTypedArray(a);
verifyRequiredAttributes(a);
@@ -127,161 +162,37 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback {
}
}
if (state.mDrawable != null && state.mDrawable.canApplyTheme()) {
state.mDrawable.applyTheme(t);
}
// The drawable may have changed as a result of applying the theme, so
// apply the theme to the wrapped drawable last.
super.applyTheme(t);
}
private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
Drawable dr = null;
int type;
final int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
if (dr != null) {
mState.mDrawable = dr;
dr.setCallback(this);
}
}
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (mState.mDrawable == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.ScaleDrawable_drawable] == 0)) {
throw new XmlPullParserException(a.getPositionDescription()
+ ": <scale> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
}
private void updateStateFromTypedArray(TypedArray a) {
final ScaleState state = mState;
// Account for any configuration changes.
state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
state.mScaleWidth = getPercent(
a, R.styleable.ScaleDrawable_scaleWidth, state.mScaleWidth);
state.mScaleHeight = getPercent(
a, R.styleable.ScaleDrawable_scaleHeight, state.mScaleHeight);
state.mGravity = a.getInt(R.styleable.ScaleDrawable_scaleGravity, state.mGravity);
state.mUseIntrinsicSizeAsMin = a.getBoolean(
R.styleable.ScaleDrawable_useIntrinsicSizeAsMinimum, state.mUseIntrinsicSizeAsMin);
final Drawable dr = a.getDrawable(R.styleable.ScaleDrawable_drawable);
if (dr != null) {
state.mDrawable = dr;
dr.setCallback(this);
}
}
@Override
public boolean canApplyTheme() {
return (mState != null && mState.canApplyTheme()) || super.canApplyTheme();
}
// overrides from Drawable.Callback
public void invalidateDrawable(Drawable who) {
if (getCallback() != null) {
getCallback().invalidateDrawable(this);
}
}
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (getCallback() != null) {
getCallback().scheduleDrawable(this, what, when);
}
}
public void unscheduleDrawable(Drawable who, Runnable what) {
if (getCallback() != null) {
getCallback().unscheduleDrawable(this, what);
}
}
// overrides from Drawable
@Override
public void draw(Canvas canvas) {
if (mState.mDrawable.getLevel() != 0)
mState.mDrawable.draw(canvas);
}
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations()
| mState.mChangingConfigurations
| mState.mDrawable.getChangingConfigurations();
}
@Override
public boolean getPadding(Rect padding) {
// XXX need to adjust padding!
return mState.mDrawable.getPadding(padding);
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
mState.mDrawable.setVisible(visible, restart);
return super.setVisible(visible, restart);
}
@Override
public void setAlpha(int alpha) {
mState.mDrawable.setAlpha(alpha);
}
@Override
public int getAlpha() {
return mState.mDrawable.getAlpha();
}
@Override
public void setColorFilter(ColorFilter cf) {
mState.mDrawable.setColorFilter(cf);
}
@Override
public void setTintList(ColorStateList tint) {
mState.mDrawable.setTintList(tint);
}
@Override
public void setTintMode(Mode tintMode) {
mState.mDrawable.setTintMode(tintMode);
final Drawable d = getDrawable();
if (d != null && d.getLevel() != 0) {
d.draw(canvas);
}
}
@Override
public int getOpacity() {
return mState.mDrawable.getOpacity();
}
final Drawable d = getDrawable();
if (d.getLevel() == 0) {
return PixelFormat.TRANSPARENT;
}
@Override
public boolean isStateful() {
return mState.mDrawable.isStateful();
}
final int opacity = d.getOpacity();
if (opacity == PixelFormat.OPAQUE && d.getLevel() < MAX_LEVEL) {
return PixelFormat.TRANSLUCENT;
}
@Override
protected boolean onStateChange(int[] state) {
boolean changed = mState.mDrawable.setState(state);
onBoundsChange(getBounds());
return changed;
return opacity;
}
@Override
protected boolean onLevelChange(int level) {
mState.mDrawable.setLevel(level);
super.onLevelChange(level);
onBoundsChange(getBounds());
invalidateSelf();
return true;
@@ -289,144 +200,67 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback {
@Override
protected void onBoundsChange(Rect bounds) {
final Drawable d = getDrawable();
final Rect r = mTmpRect;
final boolean min = mState.mUseIntrinsicSizeAsMin;
int level = getLevel();
final int level = getLevel();
int w = bounds.width();
if (mState.mScaleWidth > 0) {
final int iw = min ? mState.mDrawable.getIntrinsicWidth() : 0;
w -= (int) ((w - iw) * (10000 - level) * mState.mScaleWidth / 10000);
final int iw = min ? d.getIntrinsicWidth() : 0;
w -= (int) ((w - iw) * (MAX_LEVEL - level) * mState.mScaleWidth / MAX_LEVEL);
}
int h = bounds.height();
if (mState.mScaleHeight > 0) {
final int ih = min ? mState.mDrawable.getIntrinsicHeight() : 0;
h -= (int) ((h - ih) * (10000 - level) * mState.mScaleHeight / 10000);
final int ih = min ? d.getIntrinsicHeight() : 0;
h -= (int) ((h - ih) * (MAX_LEVEL - level) * mState.mScaleHeight / MAX_LEVEL);
}
final int layoutDirection = getLayoutDirection();
Gravity.apply(mState.mGravity, w, h, bounds, r, layoutDirection);
if (w > 0 && h > 0) {
mState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
d.setBounds(r.left, r.top, r.right, r.bottom);
}
}
@Override
public int getIntrinsicWidth() {
return mState.mDrawable.getIntrinsicWidth();
DrawableWrapperState mutateConstantState() {
mState = new ScaleState(mState);
return mState;
}
@Override
public int getIntrinsicHeight() {
return mState.mDrawable.getIntrinsicHeight();
}
@Override
public ConstantState getConstantState() {
if (mState.canConstantState()) {
mState.mChangingConfigurations = getChangingConfigurations();
return mState;
}
return null;
}
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mState.mDrawable.mutate();
mMutated = true;
}
return this;
}
/**
* @hide
*/
public void clearMutated() {
super.clearMutated();
mState.mDrawable.clearMutated();
mMutated = false;
}
final static class ScaleState extends ConstantState {
static final class ScaleState extends DrawableWrapper.DrawableWrapperState {
/** Constant used to disable scaling for a particular dimension. */
private static final float DO_NOT_SCALE = -1.0f;
int[] mThemeAttrs;
int mChangingConfigurations;
Drawable mDrawable;
float mScaleWidth = DO_NOT_SCALE;
float mScaleHeight = DO_NOT_SCALE;
int mGravity = Gravity.LEFT;
boolean mUseIntrinsicSizeAsMin = false;
private boolean mCheckedConstantState;
private boolean mCanConstantState;
ScaleState(ScaleState orig) {
super(orig);
ScaleState(ScaleState orig, ScaleDrawable owner, Resources res) {
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
if (res != null) {
mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
} else {
mDrawable = orig.mDrawable.getConstantState().newDrawable();
}
mDrawable.setCallback(owner);
mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection());
mDrawable.setBounds(orig.mDrawable.getBounds());
mDrawable.setLevel(orig.mDrawable.getLevel());
mScaleWidth = orig.mScaleWidth;
mScaleHeight = orig.mScaleHeight;
mGravity = orig.mGravity;
mUseIntrinsicSizeAsMin = orig.mUseIntrinsicSizeAsMin;
mCheckedConstantState = mCanConstantState = true;
}
}
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme())
|| super.canApplyTheme();
}
@Override
public Drawable newDrawable() {
return new ScaleDrawable(this, null);
}
@Override
public Drawable newDrawable(Resources res) {
return new ScaleDrawable(this, res);
}
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
}
boolean canConstantState() {
if (!mCheckedConstantState) {
mCanConstantState = mDrawable.getConstantState() != null;
mCheckedConstantState = true;
}
return mCanConstantState;
}
@Override
public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
final ConstantState state = mDrawable.getConstantState();
if (state != null) {
return state.addAtlasableBitmaps(atlasList);
}
return 0;
}
}
private ScaleDrawable(ScaleState state, Resources res) {
mState = new ScaleState(state, this, res);
super(state, res);
mState = state;
}
}

View File

@@ -309,16 +309,6 @@ public class StateListDrawable extends DrawableContainer {
mMutated = false;
}
/** @hide */
@Override
public void setLayoutDirection(int layoutDirection) {
super.setLayoutDirection(layoutDirection);
// Let the container handle setting its own layout direction. Otherwise,
// we're accessing potentially unused states.
mStateListState.setLayoutDirection(layoutDirection);
}
static class StateListState extends DrawableContainerState {
int[] mThemeAttrs;
int[][] mStateSets;