Merge "Fix ShapeDrawable mutate, add clone() support to ArcShape, OvalShape"

This commit is contained in:
TreeHugger Robot
2017-02-27 19:34:08 +00:00
committed by Android (Google) Code Review
10 changed files with 177 additions and 122 deletions

View File

@@ -14013,6 +14013,8 @@ package android.graphics.drawable.shapes {
public class ArcShape extends android.graphics.drawable.shapes.RectShape {
ctor public ArcShape(float, float);
method public final float getStartAngle();
method public final float getSweepAngle();
}
public class OvalShape extends android.graphics.drawable.shapes.RectShape {

View File

@@ -14649,6 +14649,8 @@ package android.graphics.drawable.shapes {
public class ArcShape extends android.graphics.drawable.shapes.RectShape {
ctor public ArcShape(float, float);
method public final float getStartAngle();
method public final float getSweepAngle();
}
public class OvalShape extends android.graphics.drawable.shapes.RectShape {

View File

@@ -14051,6 +14051,8 @@ package android.graphics.drawable.shapes {
public class ArcShape extends android.graphics.drawable.shapes.RectShape {
ctor public ArcShape(float, float);
method public final float getStartAngle();
method public final float getSweepAngle();
}
public class OvalShape extends android.graphics.drawable.shapes.RectShape {

View File

@@ -16,9 +16,11 @@
package android.graphics.drawable;
import android.annotation.NonNull;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
@@ -31,10 +33,10 @@ import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.shapes.Shape;
import android.content.res.Resources.Theme;
import android.util.AttributeSet;
import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -69,7 +71,7 @@ import java.io.IOException;
* @attr ref android.R.styleable#ShapeDrawable_height
*/
public class ShapeDrawable extends Drawable {
private ShapeState mShapeState;
private @NonNull ShapeState mShapeState;
private PorterDuffColorFilter mTintFilter;
private boolean mMutated;
@@ -77,7 +79,7 @@ public class ShapeDrawable extends Drawable {
* ShapeDrawable constructor.
*/
public ShapeDrawable() {
this(new ShapeState(null), null);
this(new ShapeState(), null);
}
/**
@@ -86,7 +88,7 @@ public class ShapeDrawable extends Drawable {
* @param s the Shape that this ShapeDrawable should be
*/
public ShapeDrawable(Shape s) {
this(new ShapeState(null), null);
this(new ShapeState(), null);
mShapeState.mShape = s;
}
@@ -402,7 +404,7 @@ public class ShapeDrawable extends Drawable {
}
// Update local properties.
updateLocalState(r);
updateLocalState();
}
@Override
@@ -426,7 +428,7 @@ public class ShapeDrawable extends Drawable {
}
// Update local properties.
updateLocalState(t.getResources());
updateLocalState();
}
private void updateStateFromTypedArray(TypedArray a) {
@@ -447,10 +449,10 @@ public class ShapeDrawable extends Drawable {
dither = a.getBoolean(R.styleable.ShapeDrawable_dither, dither);
paint.setDither(dither);
setIntrinsicWidth((int) a.getDimension(
R.styleable.ShapeDrawable_width, state.mIntrinsicWidth));
setIntrinsicHeight((int) a.getDimension(
R.styleable.ShapeDrawable_height, state.mIntrinsicHeight));
state.mIntrinsicWidth = (int) a.getDimension(
R.styleable.ShapeDrawable_width, state.mIntrinsicWidth);
state.mIntrinsicHeight = (int) a.getDimension(
R.styleable.ShapeDrawable_height, state.mIntrinsicHeight);
final int tintMode = a.getInt(R.styleable.ShapeDrawable_tintMode, -1);
if (tintMode != -1) {
@@ -494,21 +496,8 @@ public class ShapeDrawable extends Drawable {
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
if (mShapeState.mPaint != null) {
mShapeState.mPaint = new Paint(mShapeState.mPaint);
} else {
mShapeState.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
if (mShapeState.mPadding != null) {
mShapeState.mPadding = new Rect(mShapeState.mPadding);
} else {
mShapeState.mPadding = new Rect();
}
try {
mShapeState.mShape = mShapeState.mShape.clone();
} catch (CloneNotSupportedException e) {
return null;
}
mShapeState = new ShapeState(mShapeState);
updateLocalState();
mMutated = true;
}
return this;
@@ -525,12 +514,13 @@ public class ShapeDrawable extends Drawable {
/**
* Defines the intrinsic properties of this ShapeDrawable's Shape.
*/
final static class ShapeState extends ConstantState {
int[] mThemeAttrs;
static final class ShapeState extends ConstantState {
final @NonNull Paint mPaint;
@Config int mChangingConfigurations;
Paint mPaint;
int[] mThemeAttrs;
Shape mShape;
ColorStateList mTint = null;
ColorStateList mTint;
Mode mTintMode = DEFAULT_TINT_MODE;
Rect mPadding;
int mIntrinsicWidth;
@@ -538,21 +528,43 @@ public class ShapeDrawable extends Drawable {
int mAlpha = 255;
ShaderFactory mShaderFactory;
ShapeState(ShapeState orig) {
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mPaint = orig.mPaint;
mShape = orig.mShape;
mTint = orig.mTint;
mTintMode = orig.mTintMode;
mPadding = orig.mPadding;
mIntrinsicWidth = orig.mIntrinsicWidth;
mIntrinsicHeight = orig.mIntrinsicHeight;
mAlpha = orig.mAlpha;
mShaderFactory = orig.mShaderFactory;
} else {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
/**
* Constructs a new ShapeState.
*/
ShapeState() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
/**
* Constructs a new ShapeState that contains a deep copy of the
* specified ShapeState.
*
* @param orig the state to create a deep copy of
*/
ShapeState(@NonNull ShapeState orig) {
mChangingConfigurations = orig.mChangingConfigurations;
mPaint = new Paint(orig.mPaint);
mThemeAttrs = orig.mThemeAttrs;
if (mShape != null) {
try {
mShape = orig.mShape.clone();
} catch (CloneNotSupportedException e) {
// Well, at least we tried.
mShape = orig.mShape;
}
}
mTint = orig.mTint;
mTintMode = orig.mTintMode;
if (orig.mPadding != null) {
mPadding = new Rect(orig.mPadding);
}
mIntrinsicWidth = orig.mIntrinsicWidth;
mIntrinsicHeight = orig.mIntrinsicHeight;
mAlpha = orig.mAlpha;
// We don't have any way to clone a shader factory, so hopefully
// this class doesn't contain any local state.
mShaderFactory = orig.mShaderFactory;
}
@Override
@@ -585,7 +597,7 @@ public class ShapeDrawable extends Drawable {
private ShapeDrawable(ShapeState state, Resources res) {
mShapeState = state;
updateLocalState(res);
updateLocalState();
}
/**
@@ -593,7 +605,7 @@ public class ShapeDrawable extends Drawable {
* after significant state changes, e.g. from the One True Constructor and
* after inflating or applying a theme.
*/
private void updateLocalState(Resources res) {
private void updateLocalState() {
mTintFilter = updateTintFilter(mTintFilter, mShapeState.mTint, mShapeState.mTintMode);
}
@@ -617,8 +629,4 @@ public class ShapeDrawable extends Drawable {
*/
public abstract Shader resize(int width, int height);
}
// other subclass could wack the Shader's localmatrix based on the
// resize params (e.g. scaletofit, etc.). This could be used to scale
// a bitmap to fill the bounds without needing any other special casing.
}

View File

@@ -21,31 +21,47 @@ import android.graphics.Outline;
import android.graphics.Paint;
/**
* Creates an arc shape. The arc shape starts at a specified
* angle and sweeps clockwise, drawing slices of pie.
* The arc can be drawn to a Canvas with its own draw() method,
* but more graphical control is available if you instead pass
* the ArcShape to a {@link android.graphics.drawable.ShapeDrawable}.
* Creates an arc shape. The arc shape starts at a specified angle and sweeps
* clockwise, drawing slices of pie.
* <p>
* The arc can be drawn to a {@link Canvas} with its own
* {@link #draw(Canvas, Paint)} method, but more graphical control is available
* if you instead pass the ArcShape to a
* {@link android.graphics.drawable.ShapeDrawable}.
*/
public class ArcShape extends RectShape {
private float mStart;
private float mSweep;
private final float mStartAngle;
private final float mSweepAngle;
/**
* ArcShape constructor.
*
* ArcShape constructor.
*
* @param startAngle the angle (in degrees) where the arc begins
* @param sweepAngle the sweep angle (in degrees). Anything equal to or
* @param sweepAngle the sweep angle (in degrees). Anything equal to or
* greater than 360 results in a complete circle/oval.
*/
public ArcShape(float startAngle, float sweepAngle) {
mStart = startAngle;
mSweep = sweepAngle;
mStartAngle = startAngle;
mSweepAngle = sweepAngle;
}
/**
* @return the angle (in degrees) where the arc begins
*/
public final float getStartAngle() {
return mStartAngle;
}
/**
* @return the sweep angle (in degrees)
*/
public final float getSweepAngle() {
return mSweepAngle;
}
@Override
public void draw(Canvas canvas, Paint paint) {
canvas.drawArc(rect(), mStart, mSweep, true, paint);
canvas.drawArc(rect(), mStartAngle, mSweepAngle, true, paint);
}
@Override
@@ -53,5 +69,10 @@ public class ArcShape extends RectShape {
// Since we don't support concave outlines, arc shape does not attempt
// to provide an outline.
}
@Override
public ArcShape clone() throws CloneNotSupportedException {
return (ArcShape) super.clone();
}
}

View File

@@ -22,7 +22,8 @@ import android.graphics.Paint;
import android.graphics.RectF;
/**
* Defines an oval shape.
* Defines an oval shape.
* <p>
* The oval can be drawn to a Canvas with its own draw() method,
* but more graphical control is available if you instead pass
* the OvalShape to a {@link android.graphics.drawable.ShapeDrawable}.
@@ -42,5 +43,10 @@ public class OvalShape extends RectShape {
outline.setOval((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
(int) Math.floor(rect.right), (int) Math.floor(rect.bottom));
}
@Override
public OvalShape clone() throws CloneNotSupportedException {
return (OvalShape) super.clone();
}
}

View File

@@ -16,41 +16,44 @@
package android.graphics.drawable.shapes;
import android.annotation.NonNull;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
/**
* Creates geometric paths, utilizing the {@link android.graphics.Path} class.
* <p>
* The path can be drawn to a Canvas with its own draw() method,
* but more graphical control is available if you instead pass
* the PathShape to a {@link android.graphics.drawable.ShapeDrawable}.
*/
public class PathShape extends Shape {
private Path mPath;
private float mStdWidth;
private float mStdHeight;
private float mScaleX; // cached from onResize
private float mScaleY; // cached from onResize
private final float mStdWidth;
private final float mStdHeight;
private Path mPath;
private float mScaleX; // cached from onResize
private float mScaleY; // cached from onResize
/**
* PathShape constructor.
*
* @param path a Path that defines the geometric paths for this shape
* @param stdWidth the standard width for the shape. Any changes to the
* width with resize() will result in a width scaled based
* on the new width divided by this width.
* @param stdHeight the standard height for the shape. Any changes to the
* height with resize() will result in a height scaled based
* on the new height divided by this height.
*
* @param path a Path that defines the geometric paths for this shape
* @param stdWidth the standard width for the shape. Any changes to the
* width with resize() will result in a width scaled based
* on the new width divided by this width.
* @param stdHeight the standard height for the shape. Any changes to the
* height with resize() will result in a height scaled based
* on the new height divided by this height.
*/
public PathShape(Path path, float stdWidth, float stdHeight) {
public PathShape(@NonNull Path path, float stdWidth, float stdHeight) {
mPath = path;
mStdWidth = stdWidth;
mStdHeight = stdHeight;
}
@Override
public void draw(Canvas canvas, Paint paint) {
canvas.save();
@@ -58,7 +61,7 @@ public class PathShape extends Shape {
canvas.drawPath(mPath, paint);
canvas.restore();
}
@Override
protected void onResize(float width, float height) {
mScaleX = width / mStdWidth;
@@ -67,7 +70,7 @@ public class PathShape extends Shape {
@Override
public PathShape clone() throws CloneNotSupportedException {
PathShape shape = (PathShape) super.clone();
final PathShape shape = (PathShape) super.clone();
shape.mPath = new Path(mPath);
return shape;
}

View File

@@ -23,6 +23,7 @@ import android.graphics.RectF;
/**
* Defines a rectangle shape.
* <p>
* The rectangle can be drawn to a Canvas with its own draw() method,
* but more graphical control is available if you instead pass
* the RectShape to a {@link android.graphics.drawable.ShapeDrawable}.

View File

@@ -16,6 +16,7 @@
package android.graphics.drawable.shapes;
import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -24,40 +25,41 @@ import android.graphics.RectF;
/**
* Creates a rounded-corner rectangle. Optionally, an inset (rounded) rectangle
* can be included (to make a sort of "O" shape).
* can be included (to make a sort of "O" shape).
* <p>
* The rounded rectangle can be drawn to a Canvas with its own draw() method,
* but more graphical control is available if you instead pass
* the RoundRectShape to a {@link android.graphics.drawable.ShapeDrawable}.
*/
public class RoundRectShape extends RectShape {
private float[] mOuterRadii;
private RectF mInset;
private RectF mInset;
private float[] mInnerRadii;
private RectF mInnerRect;
private Path mPath; // this is what we actually draw
private Path mPath; // this is what we actually draw
/**
* RoundRectShape constructor.
* <p>
* Specifies an outer (round)rect and an optional inner (round)rect.
*
* @param outerRadii An array of 8 radius values, for the outer roundrect.
* The first two floats are for the
* top-left corner (remaining pairs correspond clockwise).
* For no rounded corners on the outer rectangle,
* pass null.
* @param inset A RectF that specifies the distance from the inner
* rect to each side of the outer rect.
* For no inner, pass null.
* The first two floats are for the top-left corner
* (remaining pairs correspond clockwise). For no rounded
* corners on the outer rectangle, pass {@code null}.
* @param inset A RectF that specifies the distance from the inner
* rect to each side of the outer rect. For no inner, pass
* {@code null}.
* @param innerRadii An array of 8 radius values, for the inner roundrect.
* The first two floats are for the
* top-left corner (remaining pairs correspond clockwise).
* For no rounded corners on the inner rectangle,
* pass null.
* If inset parameter is null, this parameter is ignored.
* The first two floats are for the top-left corner
* (remaining pairs correspond clockwise). For no rounded
* corners on the inner rectangle, pass {@code null}. If
* inset parameter is {@code null}, this parameter is
* ignored.
*/
public RoundRectShape(float[] outerRadii, RectF inset,
float[] innerRadii) {
public RoundRectShape(@Nullable float[] outerRadii, @Nullable RectF inset,
@Nullable float[] innerRadii) {
if (outerRadii != null && outerRadii.length < 8) {
throw new ArrayIndexOutOfBoundsException("outer radii must have >= 8 values");
}
@@ -97,8 +99,7 @@ public class RoundRectShape extends RectShape {
final RectF rect = rect();
outline.setRoundRect((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
(int) Math.floor(rect.right), (int) Math.floor(rect.bottom),
radius);
(int) Math.floor(rect.right), (int) Math.floor(rect.bottom), radius);
}
@Override
@@ -128,7 +129,7 @@ public class RoundRectShape extends RectShape {
@Override
public RoundRectShape clone() throws CloneNotSupportedException {
RoundRectShape shape = (RoundRectShape) super.clone();
final RoundRectShape shape = (RoundRectShape) super.clone();
shape.mOuterRadii = mOuterRadii != null ? mOuterRadii.clone() : null;
shape.mInnerRadii = mInnerRadii != null ? mInnerRadii.clone() : null;
shape.mInset = new RectF(mInset);

View File

@@ -23,21 +23,25 @@ import android.graphics.Paint;
/**
* Defines a generic graphical "shape."
* Any Shape can be drawn to a Canvas with its own draw() method,
* but more graphical control is available if you instead pass
* it to a {@link android.graphics.drawable.ShapeDrawable}.
* <p>
* Any Shape can be drawn to a Canvas with its own draw() method, but more
* graphical control is available if you instead pass it to a
* {@link android.graphics.drawable.ShapeDrawable}.
* <p>
* Custom Shape classes must implement {@link #clone()} and return an instance
* of the custom Shape class.
*/
public abstract class Shape implements Cloneable {
private float mWidth;
private float mHeight;
/**
* Returns the width of the Shape.
*/
public final float getWidth() {
return mWidth;
}
/**
* Returns the height of the Shape.
*/
@@ -46,9 +50,10 @@ public abstract class Shape implements Cloneable {
}
/**
* Draw this shape into the provided Canvas, with the provided Paint.
* Draws this shape into the provided Canvas, with the provided Paint.
* <p>
* Before calling this, you must call {@link #resize(float,float)}.
*
*
* @param canvas the Canvas within which this shape should be drawn
* @param paint the Paint object that defines this shape's characteristics
*/
@@ -56,8 +61,9 @@ public abstract class Shape implements Cloneable {
/**
* Resizes the dimensions of this shape.
* <p>
* Must be called before {@link #draw(Canvas,Paint)}.
*
*
* @param width the width of the shape (in pixels)
* @param height the height of the shape (in pixels)
*/
@@ -74,30 +80,34 @@ public abstract class Shape implements Cloneable {
onResize(width, height);
}
}
/**
* Checks whether the Shape is opaque.
* Default impl returns true. Override if your subclass can be opaque.
*
* @return true if any part of the drawable is <em>not</em> opaque.
* <p>
* Default impl returns {@code true}. Override if your subclass can be
* opaque.
*
* @return true if any part of the drawable is <em>not</em> opaque.
*/
public boolean hasAlpha() {
return true;
}
/**
* Callback method called when {@link #resize(float,float)} is executed.
*
*
* @param width the new width of the Shape
* @param height the new height of the Shape
*/
protected void onResize(float width, float height) {}
/**
* Compute the Outline of the shape and return it in the supplied Outline
* parameter. The default implementation does nothing and {@code outline} is not changed.
* Computes the Outline of the shape and return it in the supplied Outline
* parameter. The default implementation does nothing and {@code outline}
* is not changed.
*
* @param outline The Outline to be populated with the result. Should not be null.
* @param outline the Outline to be populated with the result. Must be
* non-{@code null}.
*/
public void getOutline(@NonNull Outline outline) {}
@@ -105,5 +115,4 @@ public abstract class Shape implements Cloneable {
public Shape clone() throws CloneNotSupportedException {
return (Shape) super.clone();
}
}