Merge "Unify wrapper-type drawables"
This commit is contained in:
committed by
Android (Google) Code Review
commit
b3f3762ee0
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
443
graphics/java/android/graphics/drawable/DrawableWrapper.java
Normal file
443
graphics/java/android/graphics/drawable/DrawableWrapper.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -614,7 +614,6 @@ public class RippleDrawable extends LayerDrawable {
|
||||
onHotspotBoundsChanged();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public void getHotspotBounds(Rect outRect) {
|
||||
outRect.set(mHotspotBounds);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user