diff --git a/api/current.txt b/api/current.txt index 1eb37b3a45c47..d1cfb62c60070 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12302,6 +12302,7 @@ package android.graphics.drawable { method public int getIntrinsicWidth(); method public int getLayoutDirection(); method public final int getLevel(); + method public final float getLevelFloat(); method public int getMinimumHeight(); method public int getMinimumWidth(); method public abstract int getOpacity(); @@ -12321,6 +12322,7 @@ package android.graphics.drawable { method protected void onBoundsChange(android.graphics.Rect); method public boolean onLayoutDirectionChanged(int); method protected boolean onLevelChange(int); + method protected boolean onLevelChange(float); method protected boolean onStateChange(int[]); method public static int resolveOpacity(int, int); method public void scheduleSelf(java.lang.Runnable, long); @@ -12338,12 +12340,15 @@ package android.graphics.drawable { method public void setHotspotBounds(int, int, int, int); method public final boolean setLayoutDirection(int); method public final boolean setLevel(int); + method public final boolean setLevel(float); method public boolean setState(int[]); method public void setTint(int); method public void setTintList(android.content.res.ColorStateList); method public void setTintMode(android.graphics.PorterDuff.Mode); method public boolean setVisible(boolean, boolean); method public void unscheduleSelf(java.lang.Runnable); + field public static final int MAX_LEVEL = 10000; // 0x2710 + field public static final float MAX_LEVEL_FLOAT = 10000.0f; } public static abstract interface Drawable.Callback { diff --git a/api/system-current.txt b/api/system-current.txt index f913e6c99b2cc..8bd0181804768 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -12639,6 +12639,7 @@ package android.graphics.drawable { method public int getIntrinsicWidth(); method public int getLayoutDirection(); method public final int getLevel(); + method public final float getLevelFloat(); method public int getMinimumHeight(); method public int getMinimumWidth(); method public abstract int getOpacity(); @@ -12658,6 +12659,7 @@ package android.graphics.drawable { method protected void onBoundsChange(android.graphics.Rect); method public boolean onLayoutDirectionChanged(int); method protected boolean onLevelChange(int); + method protected boolean onLevelChange(float); method protected boolean onStateChange(int[]); method public static int resolveOpacity(int, int); method public void scheduleSelf(java.lang.Runnable, long); @@ -12675,12 +12677,15 @@ package android.graphics.drawable { method public void setHotspotBounds(int, int, int, int); method public final boolean setLayoutDirection(int); method public final boolean setLevel(int); + method public final boolean setLevel(float); method public boolean setState(int[]); method public void setTint(int); method public void setTintList(android.content.res.ColorStateList); method public void setTintMode(android.graphics.PorterDuff.Mode); method public boolean setVisible(boolean, boolean); method public void unscheduleSelf(java.lang.Runnable); + field public static final int MAX_LEVEL = 10000; // 0x2710 + field public static final float MAX_LEVEL_FLOAT = 10000.0f; } public static abstract interface Drawable.Callback { diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 1857345968fd7..abb51dbac1b3f 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -217,7 +217,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } @Override - protected boolean onLevelChange(int level) { + protected boolean onLevelChange(float level) { return mAnimatedVectorState.mVectorDrawable.setLevel(level); } diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java index 31fccd0880c9b..3b9250795992a 100644 --- a/graphics/java/android/graphics/drawable/ClipDrawable.java +++ b/graphics/java/android/graphics/drawable/ClipDrawable.java @@ -52,8 +52,6 @@ public class ClipDrawable extends DrawableWrapper { public static final int HORIZONTAL = 1; public static final int VERTICAL = 2; - private static final int MAX_LEVEL = 10000; - private final Rect mTmpRect = new Rect(); private ClipState mState; @@ -143,7 +141,7 @@ public class ClipDrawable extends DrawableWrapper { } @Override - protected boolean onLevelChange(int level) { + protected boolean onLevelChange(float level) { super.onLevelChange(level); invalidateSelf(); return true; @@ -153,12 +151,12 @@ public class ClipDrawable extends DrawableWrapper { public int getOpacity() { final Drawable dr = getDrawable(); final int opacity = dr.getOpacity(); - if (opacity == PixelFormat.TRANSPARENT || dr.getLevel() == 0) { + if (opacity == PixelFormat.TRANSPARENT || dr.getLevelFloat() == 0) { return PixelFormat.TRANSPARENT; } - final int level = getLevel(); - if (level >= MAX_LEVEL) { + final float level = getLevelFloat(); + if (level >= MAX_LEVEL_FLOAT) { return dr.getOpacity(); } @@ -169,24 +167,24 @@ public class ClipDrawable extends DrawableWrapper { @Override public void draw(Canvas canvas) { final Drawable dr = getDrawable(); - if (dr.getLevel() == 0) { + if (dr.getLevelFloat() == 0) { return; } final Rect r = mTmpRect; final Rect bounds = getBounds(); - final int level = getLevel(); + final float level = getLevelFloat(); int w = bounds.width(); - final int iw = 0; //mState.mDrawable.getIntrinsicWidth(); + final int iw = 0; if ((mState.mOrientation & HORIZONTAL) != 0) { - w -= (w - iw) * (MAX_LEVEL - level) / MAX_LEVEL; + w -= Math.round((w - iw) * (MAX_LEVEL_FLOAT - level) / MAX_LEVEL_FLOAT); } int h = bounds.height(); - final int ih = 0; //mState.mDrawable.getIntrinsicHeight(); + final int ih = 0; if ((mState.mOrientation & VERTICAL) != 0) { - h -= (h - ih) * (MAX_LEVEL - level) / MAX_LEVEL; + h -= Math.round((h - ih) * (MAX_LEVEL_FLOAT - level) / MAX_LEVEL_FLOAT); } final int layoutDirection = getLayoutDirection(); diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index b95c183642ca2..fb7715507103c 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -41,6 +41,7 @@ import android.graphics.Xfermode; import android.os.Trace; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.FloatProperty; import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; @@ -126,12 +127,19 @@ import java.util.Collection; * document.
*/ public abstract class Drawable { + private static final Rect ZERO_BOUNDS_RECT = new Rect(); static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN; + /** The standard maximum value for calls to {@link #setLevel(int)}. */ + public static final int MAX_LEVEL = 10000; + + /** The standard maximum value for calls to {@link #setLevel(float)}. */ + public static final float MAX_LEVEL_FLOAT = 10000.0f; + private int[] mStateSet = StateSet.WILD_CARD; - private int mLevel = 0; + private float mLevel = 0.0f; private int mChangingConfigurations = 0; private Rect mBounds = ZERO_BOUNDS_RECT; // lazily becomes a new Rect() private WeakReference+ * This allows a drawable to vary its imagery based on a continuous + * controller. For example, it may be used to show progress or volume + * level. + *
+ * Use #setLevelFloat(float) to set the level as a high-precision + * floating-point value. * - *
If the new level you are supplying causes the appearance of the - * Drawable to change, then it is responsible for calling - * {@link #invalidateSelf} in order to have itself redrawn, and - * true will be returned from this function. - * - * @param level The new level, from 0 (minimum) to 10000 (maximum). - * - * @return Returns true if this change in level has caused the appearance - * of the Drawable to change (hence requiring an invalidate), otherwise - * returns false. + * @param level the new level, typically between 0 and 10000 + * @return {@code true} if this change in level has caused the appearance + * of the drawable to change and will require a subsequent call to + * invalidate, {@code false} otherwise + * @see #getLevel() + * @see #setLevel(float) + * @see #onLevelChange(int) */ public final boolean setLevel(int level) { + return setLevel((float) level); + } + + /** + * Returns the current level as a rounded integer value. + *
+ * Use #getLevelFloat() to return the level as a high-precision + * floating-point value. + * + * @return the current level, typically between 0 and 10000 + * @see #setLevel(int) + * @see #getLevelFloat() + */ + public final int getLevel() { + return Math.round(mLevel); + } + + /** + * Sets the level for the drawable as a floating-point value where + * typically the minimum level is 0.0 and the maximum is 10000.0 + * {@link #MAX_LEVEL_FLOAT}; however, the range may vary based on the + * Drawable implementation and is not clamped. + *
+ * This allows a drawable to vary its imagery based on a continuous + * controller. For example, it may be used to show progress or volume + * level. + * + * @param level the new level, typically between 0.0 and 10000.0 + * ({@link #MAX_LEVEL_FLOAT}) + * @return {@code true} if this change in level has caused the appearance + * of the drawable to change and will require a subsequent call to + * invalidate, {@code false} otherwise + * @see #getLevelFloat() + * @see #onLevelChange(float) + */ + public final boolean setLevel(float level) { if (mLevel != level) { mLevel = level; return onLevelChange(level); @@ -735,11 +784,13 @@ public abstract class Drawable { } /** - * Retrieve the current level. + * Returns the current level as a floating-point value. * - * @return int Current level, from 0 (minimum) to 10000 (maximum). + * @return the current level, typically between 0.0 and 10000.0 + * ({@link #MAX_LEVEL_FLOAT}) + * @see #setLevel(float) */ - public final int getLevel() { + public final float getLevelFloat() { return mLevel; } @@ -894,14 +945,47 @@ public abstract class Drawable { * last state. */ protected boolean onStateChange(int[] state) { return false; } - /** Override this in your subclass to change appearance if you vary based - * on level. - * @return Returns true if the level change has caused the appearance of - * the Drawable to change (that is, it needs to be drawn), else false - * if it looks the same and there is no need to redraw it since its - * last level. + + /** + * Called when the drawable level changes. + *
+ * Override this in your subclass to change appearance if you vary based on + * level and do not need floating-point accuracy. To handle changes with + * higher accuracy, override {@link #onLevelChange(float)} instead. + *
+ * Note: Do not override both this method and + * {@link #onLevelChange(float)}. Only override one method. + * + * @param level the level as an integer value, typically between 0 + * (minimum) and 10000 ({@link #MAX_LEVEL}) + * @return {@code true} if the level change has caused the appearance of + * the drawable to change such that it needs to be redrawn, or + * {@code false} if there is no need to redraw */ protected boolean onLevelChange(int level) { return false; } + + /** + * Called when the drawable level changes. + *
+ * Override this in your subclass to change appearance if you vary based on + * level and need floating-point accuracy. + *
+ * Note: Do not override both this method and
+ * {@link #onLevelChange(int)}. Only override one method. If your app
+ * targets SDK <= 23 ({@link android.os.Build.VERSION_CODES#M M}), you
+ * will need to override {@link #onLevelChange(int)} to receive callbacks
+ * on devices running Android M and below.
+ *
+ * @param level the level as a floating-point value, typically between 0.0
+ * and 10000.0 ({@link #MAX_LEVEL_FLOAT})
+ * @return {@code true} if the level change has caused the appearance of
+ * the drawable to change such that it needs to be redrawn, or
+ * {@code false} if there is no need to redraw
+ */
+ protected boolean onLevelChange(float level) {
+ return onLevelChange(Math.round(level));
+ }
+
/**
* Override this in your subclass to change appearance if you vary based on
* the bounds.
@@ -1327,6 +1411,23 @@ public abstract class Drawable {
return tintFilter;
}
+ /**
+ * Animatable property for Drawable level.
+ *
+ * @hide Until Drawable animations have been cleaned up.
+ */
+ public static final FloatProperty