diff options
author | Alan Viverette <alanv@google.com> | 2015-02-23 20:38:58 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-02-23 20:39:01 +0000 |
commit | b3f3762ee0f57c94ea25bc1cc9d4baf0ee2a2976 (patch) | |
tree | 91d4c19db8c884df6d55a2b2c31e87c84bda3708 /graphics | |
parent | db871da48fe65b33bfaf5fb6c0bf328b291a667e (diff) | |
parent | a12962207155305da44b5a1b8fb9acaed358c14c (diff) | |
download | frameworks_base-b3f3762ee0f57c94ea25bc1cc9d4baf0ee2a2976.zip frameworks_base-b3f3762ee0f57c94ea25bc1cc9d4baf0ee2a2976.tar.gz frameworks_base-b3f3762ee0f57c94ea25bc1cc9d4baf0ee2a2976.tar.bz2 |
Merge "Unify wrapper-type drawables"
Diffstat (limited to 'graphics')
12 files changed, 866 insertions, 1430 deletions
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java index d2799e1..db94d89 100644 --- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java @@ -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); - - init(); + updateLocalState(); } - @Override - public void applyTheme(@Nullable Theme t) { - super.applyTheme(t); - - 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); + 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"); } - - init(); } - private void updateStateFromTypedArray(TypedArray a) { - final AnimatedRotateState state = mState; - - // Account for any configuration changes. - state.mChangingConfigurations |= a.getChangingConfigurations(); + @Override + void updateStateFromTypedArray(TypedArray a) { + super.updateStateFromTypedArray(a); - // Extract the theme attributes, if any. - state.mThemeAttrs = a.extractThemeAttrs(); + final AnimatedRotateState state = mState; 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,35 +223,20 @@ 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); + } + } + } } diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 2a17a60..17e5b0b 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -197,6 +197,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { } @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(); diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java index e5b2b76..f3574e8 100644 --- a/graphics/java/android/graphics/drawable/ClipDrawable.java +++ b/graphics/java/android/graphics/drawable/ClipDrawable.java @@ -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) { - final ClipState state = mState; - - // Account for any configuration changes. - state.mChangingConfigurations |= a.getChangingConfigurations(); - - // Extract the theme attributes, if any. - state.mThemeAttrs = a.extractThemeAttrs(); + @Override + void updateStateFromTypedArray(TypedArray a) { + super.updateStateFromTypedArray(a); - state.mOrientation = a.getInt(R.styleable.ClipDrawable_clipOrientation, state.mOrientation); - state.mGravity = a.getInt(R.styleable.ClipDrawable_gravity, state.mGravity); + final ClipState state = mState; + 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.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); + 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(); + } } - } - - // 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; } } diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index c9d7869..98767a7 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -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()); } diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 557f563..70693f2 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -306,7 +306,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } } - /** @hide */ @Override public void getHotspotBounds(Rect outRect) { if (mHotspotBounds != null) { @@ -339,6 +338,13 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } @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()) { return mDrawableContainerState.getConstantWidth(); @@ -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) { diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java new file mode 100644 index 0000000..b17bab0 --- /dev/null +++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java @@ -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; + } + } +} diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java index 50a6df4..b0cd386 100644 --- a/graphics/java/android/graphics/drawable/InsetDrawable.java +++ b/graphics/java/android/graphics/drawable/InsetDrawable.java @@ -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 { - final InsetState state = mState; - - // Account for any configuration changes. - state.mChangingConfigurations |= a.getChangingConfigurations(); - - // Extract the theme attributes, if any. - state.mThemeAttrs = a.extractThemeAttrs(); + @Override + void updateStateFromTypedArray(TypedArray a) { + super.updateStateFromTypedArray(a); + final InsetState state = mState; 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; @@ -303,62 +202,9 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { } @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; @@ -367,23 +213,6 @@ public class InsetDrawable extends Drawable implements Drawable.Callback { } @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; r.set(bounds); @@ -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; @@ -475,39 +283,9 @@ 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) { - mState = state; - - updateLocalState(res); - } + super(state, 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); - } + mState = state; } } diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 616aebd..08849df 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -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 { diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 556c59f..c6ea91d 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -614,7 +614,6 @@ public class RippleDrawable extends LayerDrawable { onHotspotBoundsChanged(); } - /** @hide */ @Override public void getHotspotBounds(Rect outRect) { outRect.set(mHotspotBounds); diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java index e1991fe..595061c 100644 --- a/graphics/java/android/graphics/drawable/RotateDrawable.java +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -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,100 +64,108 @@ 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; + 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); - final int saveCount = canvas.save(); - canvas.rotate(st.mCurrentDegrees, px + bounds.left, py + bounds.top); - d.draw(canvas); - canvas.restoreToCount(saveCount); + updateStateFromTypedArray(a); + inflateChildDrawable(r, parser, attrs, theme); + verifyRequiredAttributes(a); + a.recycle(); } - /** - * 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); - } + 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.RotateDrawable_drawable] == 0)) { + throw new XmlPullParserException(a.getPositionDescription() + + ": <rotate> tag requires a 'drawable' attribute or " + + "child tag defining a drawable"); } } - /** - * @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(); - } + void updateStateFromTypedArray(TypedArray a) { + super.updateStateFromTypedArray(a); - @Override - public void setAlpha(int alpha) { - mState.mDrawable.setAlpha(alpha); - } + final RotateState state = mState; - @Override - public int getAlpha() { - return mState.mDrawable.getAlpha(); - } + // Account for any configuration changes. + state.mChangingConfigurations |= a.getChangingConfigurations(); - @Override - public void setColorFilter(ColorFilter cf) { - mState.mDrawable.setColorFilter(cf); - } + // Extract the theme attributes, if any. + state.mThemeAttrs = a.extractThemeAttrs(); - @Override - public void setTintList(ColorStateList tint) { - mState.mDrawable.setTintList(tint); + if (a.hasValue(R.styleable.RotateDrawable_pivotX)) { + final TypedValue tv = a.peekValue(R.styleable.RotateDrawable_pivotX); + state.mPivotXRel = tv.type == TypedValue.TYPE_FRACTION; + state.mPivotX = state.mPivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); + } + + if (a.hasValue(R.styleable.RotateDrawable_pivotY)) { + final TypedValue tv = a.peekValue(R.styleable.RotateDrawable_pivotY); + state.mPivotYRel = tv.type == TypedValue.TYPE_FRACTION; + 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.mCurrentDegrees = state.mFromDegrees; + + final Drawable dr = a.getDrawable(R.styleable.RotateDrawable_drawable); + if (dr != null) { + setDrawable(dr); + } } @Override - public void setTintMode(Mode tintMode) { - mState.mDrawable.setTintMode(tintMode); + public void applyTheme(Theme 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(); + } + } + + // 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 int getOpacity() { - return mState.mDrawable.getOpacity(); + 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); } /** * Sets the start angle for rotation. * - * @param fromDegrees Starting angle in degrees - * + * @param fromDegrees starting angle in degrees * @see #getFromDegrees() * @attr ref android.R.styleable#RotateDrawable_fromDegrees */ @@ -170,8 +177,7 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { } /** - * @return The starting angle for rotation in degrees - * + * @return starting angle for rotation in degrees * @see #setFromDegrees(float) * @attr ref android.R.styleable#RotateDrawable_fromDegrees */ @@ -182,8 +188,7 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { /** * Sets the end angle for rotation. * - * @param toDegrees Ending angle in degrees - * + * @param toDegrees ending angle in degrees * @see #getToDegrees() * @attr ref android.R.styleable#RotateDrawable_toDegrees */ @@ -195,8 +200,7 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { } /** - * @return The ending angle for rotation in degrees - * + * @return ending angle for rotation in degrees * @see #setToDegrees(float) * @attr ref android.R.styleable#RotateDrawable_toDegrees */ @@ -211,7 +215,6 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { * 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 */ @@ -224,7 +227,6 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { /** * @return X position around which to rotate - * * @see #setPivotX(float) * @attr ref android.R.styleable#RotateDrawable_pivotX */ @@ -236,9 +238,8 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { * 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 + * @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) { @@ -249,9 +250,8 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { } /** - * @return True if the X pivot represents a fraction of the drawable width, + * @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() { @@ -265,7 +265,6 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { * 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 */ @@ -278,7 +277,6 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { /** * @return Y position around which to rotate - * * @see #setPivotY(float) * @attr ref android.R.styleable#RotateDrawable_pivotY */ @@ -292,7 +290,6 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { * * @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) { @@ -303,9 +300,8 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { } /** - * @return True if the Y pivot represents a fraction of the drawable height, + * @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() { @@ -313,254 +309,36 @@ public class RotateDrawable extends Drawable implements Drawable.Callback { } @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()); + super.onLevelChange(level); - mState.mCurrentDegrees = mState.mFromDegrees + - (mState.mToDegrees - mState.mFromDegrees) * - (level / MAX_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 - 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); - 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)) { - throw new XmlPullParserException(a.getPositionDescription() - + ": <rotate> tag requires a 'drawable' attribute or " - + "child tag defining a drawable"); - } - } - - private void updateStateFromTypedArray(TypedArray a) { - final RotateState 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.RotateDrawable_pivotX)) { - final TypedValue tv = a.peekValue(R.styleable.RotateDrawable_pivotX); - state.mPivotXRel = tv.type == TypedValue.TYPE_FRACTION; - state.mPivotX = state.mPivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); - } - - if (a.hasValue(R.styleable.RotateDrawable_pivotY)) { - final TypedValue tv = a.peekValue(R.styleable.RotateDrawable_pivotY); - state.mPivotYRel = tv.type == TypedValue.TYPE_FRACTION; - 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.mCurrentDegrees = state.mFromDegrees; - - final Drawable dr = a.getDrawable(R.styleable.RotateDrawable_drawable); - if (dr != null) { - state.mDrawable = dr; - dr.setCallback(this); - } - } - - @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; + DrawableWrapperState mutateConstantState() { + mState = new RotateState(mState); + return mState; } - /** - * 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. - */ - final static class RotateState extends Drawable.ConstantState { - int[] mThemeAttrs; - int mChangingConfigurations; - - Drawable mDrawable; - + 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; - } - - public boolean canConstantState() { - if (!mCheckedConstantState) { - mCanConstantState = mDrawable.getConstantState() != null; - mCheckedConstantState = true; - } + private RotateDrawable(RotateState state, Resources res) { + super(state, res); - return mCanConstantState; - } + mState = state; } } diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java index da722f3..0acbeda 100644 --- a/graphics/java/android/graphics/drawable/ScaleDrawable.java +++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java @@ -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,65 +88,15 @@ 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(); } - @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); - 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 + 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 " @@ -161,127 +104,95 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback { } } - 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(); + @Override + void updateStateFromTypedArray(TypedArray a) { + super.updateStateFromTypedArray(a); - 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); + 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) { - 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); + setDrawable(dr); } } - public void scheduleDrawable(Drawable who, Runnable what, long when) { - if (getCallback() != null) { - getCallback().scheduleDrawable(this, what, when); + 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); } - } - public void unscheduleDrawable(Drawable who, Runnable what) { - if (getCallback() != null) { - getCallback().unscheduleDrawable(this, what); + // 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; + } } - } - - // 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); + return defaultValue; } @Override - public int getAlpha() { - return mState.mDrawable.getAlpha(); - } + public void applyTheme(Theme t) { + final ScaleState state = mState; + if (state == null) { + return; + } - @Override - public void setColorFilter(ColorFilter cf) { - mState.mDrawable.setColorFilter(cf); - } + if (state.mThemeAttrs != null) { + final TypedArray a = t.resolveAttributes( + state.mThemeAttrs, R.styleable.ScaleDrawable); + try { + updateStateFromTypedArray(a); + verifyRequiredAttributes(a); + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } finally { + a.recycle(); + } + } - @Override - public void setTintList(ColorStateList tint) { - mState.mDrawable.setTintList(tint); + // 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 setTintMode(Mode tintMode) { - mState.mDrawable.setTintMode(tintMode); + public void draw(Canvas canvas) { + 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); - } - } - - @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; + d.setBounds(r.left, r.top, r.right, r.bottom); } - return null; } @Override - public Drawable mutate() { - if (!mMutated && super.mutate() == this) { - mState.mDrawable.mutate(); - mMutated = true; - } - return this; + DrawableWrapperState mutateConstantState() { + mState = new ScaleState(mState); + return mState; } - /** - * @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; } } diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java index 59d0ba6..c83af11 100644 --- a/graphics/java/android/graphics/drawable/StateListDrawable.java +++ b/graphics/java/android/graphics/drawable/StateListDrawable.java @@ -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; |