diff --git a/lib/java/com/google/android/material/shape/MaterialShapeDrawable.java b/lib/java/com/google/android/material/shape/MaterialShapeDrawable.java index aab97702d89..1a308fd3adb 100644 --- a/lib/java/com/google/android/material/shape/MaterialShapeDrawable.java +++ b/lib/java/com/google/android/material/shape/MaterialShapeDrawable.java @@ -208,7 +208,11 @@ public MaterialShapeDrawable(@NonNull ShapeAppearanceModel shapeAppearanceModel) this(new MaterialShapeDrawableState(shapeAppearanceModel, null)); } - private MaterialShapeDrawable(@NonNull MaterialShapeDrawableState drawableState) { + /** + * @hide + */ + @RestrictTo(LIBRARY_GROUP) + protected MaterialShapeDrawable(@NonNull MaterialShapeDrawableState drawableState) { this.drawableState = drawableState; strokePaint.setStyle(Style.STROKE); fillPaint.setStyle(Style.FILL); @@ -1394,39 +1398,45 @@ public boolean isRoundRect() { return drawableState.shapeAppearanceModel.isRoundRect(getBoundsAsRectF()); } - static final class MaterialShapeDrawableState extends ConstantState { + /** + * Drawable state for {@link MaterialShapeDrawable} + * + * @hide + */ + @RestrictTo(LIBRARY_GROUP) + protected static class MaterialShapeDrawableState extends ConstantState { - @NonNull public ShapeAppearanceModel shapeAppearanceModel; - @Nullable public ElevationOverlayProvider elevationOverlayProvider; + @NonNull ShapeAppearanceModel shapeAppearanceModel; + @Nullable ElevationOverlayProvider elevationOverlayProvider; - @Nullable public ColorFilter colorFilter; - @Nullable public ColorStateList fillColor = null; - @Nullable public ColorStateList strokeColor = null; - @Nullable public ColorStateList strokeTintList = null; - @Nullable public ColorStateList tintList = null; - @Nullable public PorterDuff.Mode tintMode = PorterDuff.Mode.SRC_IN; - @Nullable public Rect padding = null; + @Nullable ColorFilter colorFilter; + @Nullable ColorStateList fillColor = null; + @Nullable ColorStateList strokeColor = null; + @Nullable ColorStateList strokeTintList = null; + @Nullable ColorStateList tintList = null; + @Nullable PorterDuff.Mode tintMode = PorterDuff.Mode.SRC_IN; + @Nullable Rect padding = null; - public float scale = 1f; - public float interpolation = 1f; - public float strokeWidth; + float scale = 1f; + float interpolation = 1f; + float strokeWidth; - public int alpha = 255; - public float parentAbsoluteElevation = 0; - public float elevation = 0; - public float translationZ = 0; - public int shadowCompatMode = SHADOW_COMPAT_MODE_DEFAULT; - public int shadowCompatRadius = 0; - public int shadowCompatOffset = 0; - public int shadowCompatRotation = 0; + int alpha = 255; + float parentAbsoluteElevation = 0; + float elevation = 0; + float translationZ = 0; + int shadowCompatMode = SHADOW_COMPAT_MODE_DEFAULT; + int shadowCompatRadius = 0; + int shadowCompatOffset = 0; + int shadowCompatRotation = 0; - public boolean useTintColorForShadow = false; + boolean useTintColorForShadow = false; - public Style paintStyle = Style.FILL_AND_STROKE; + Style paintStyle = Style.FILL_AND_STROKE; public MaterialShapeDrawableState( - ShapeAppearanceModel shapeAppearanceModel, - ElevationOverlayProvider elevationOverlayProvider) { + @NonNull ShapeAppearanceModel shapeAppearanceModel, + @Nullable ElevationOverlayProvider elevationOverlayProvider) { this.shapeAppearanceModel = shapeAppearanceModel; this.elevationOverlayProvider = elevationOverlayProvider; } diff --git a/lib/java/com/google/android/material/textfield/CutoutDrawable.java b/lib/java/com/google/android/material/textfield/CutoutDrawable.java index 15dda0f19e2..6d8130fc63f 100644 --- a/lib/java/com/google/android/material/textfield/CutoutDrawable.java +++ b/lib/java/com/google/android/material/textfield/CutoutDrawable.java @@ -25,6 +25,7 @@ import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.Region.Op; +import android.graphics.drawable.Drawable; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.view.View; @@ -38,31 +39,44 @@ * outline mode. */ class CutoutDrawable extends MaterialShapeDrawable { - @NonNull protected final RectF cutoutBounds; + @NonNull CutoutDrawableState drawableState; static CutoutDrawable create(@Nullable ShapeAppearanceModel shapeAppearanceModel) { + return create(new CutoutDrawableState( + shapeAppearanceModel != null ? shapeAppearanceModel : new ShapeAppearanceModel(), + new RectF())); + } + + private static CutoutDrawable create(@NonNull CutoutDrawableState drawableState) { return VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2 - ? new ImplApi18(shapeAppearanceModel) - : new ImplApi14(shapeAppearanceModel); + ? new ImplApi18(drawableState) + : new ImplApi14(drawableState); + } + + private CutoutDrawable(@NonNull CutoutDrawableState drawableState) { + super(drawableState); + this.drawableState = drawableState; } - private CutoutDrawable(@Nullable ShapeAppearanceModel shapeAppearanceModel) { - super(shapeAppearanceModel != null ? shapeAppearanceModel : new ShapeAppearanceModel()); - cutoutBounds = new RectF(); + @NonNull + @Override + public Drawable mutate() { + drawableState = new CutoutDrawableState(drawableState); + return this; } boolean hasCutout() { - return !cutoutBounds.isEmpty(); + return !drawableState.cutoutBounds.isEmpty(); } void setCutout(float left, float top, float right, float bottom) { // Avoid expensive redraws by only calling invalidateSelf if one of the cutout's dimensions has // changed. - if (left != cutoutBounds.left - || top != cutoutBounds.top - || right != cutoutBounds.right - || bottom != cutoutBounds.bottom) { - cutoutBounds.set(left, top, right, bottom); + if (left != drawableState.cutoutBounds.left + || top != drawableState.cutoutBounds.top + || right != drawableState.cutoutBounds.right + || bottom != drawableState.cutoutBounds.bottom) { + drawableState.cutoutBounds.set(left, top, right, bottom); invalidateSelf(); } } @@ -78,21 +92,21 @@ void removeCutout() { @TargetApi(VERSION_CODES.JELLY_BEAN_MR2) private static class ImplApi18 extends CutoutDrawable { - ImplApi18(@Nullable ShapeAppearanceModel shapeAppearanceModel) { - super(shapeAppearanceModel); + ImplApi18(@NonNull CutoutDrawableState drawableState) { + super(drawableState); } @Override protected void drawStrokeShape(@NonNull Canvas canvas) { - if (cutoutBounds.isEmpty()) { + if (drawableState.cutoutBounds.isEmpty()) { super.drawStrokeShape(canvas); } else { // Saves the canvas so we can restore the clip after drawing the stroke. canvas.save(); if (VERSION.SDK_INT >= VERSION_CODES.O) { - canvas.clipOutRect(cutoutBounds); + canvas.clipOutRect(drawableState.cutoutBounds); } else { - canvas.clipRect(cutoutBounds, Op.DIFFERENCE); + canvas.clipRect(drawableState.cutoutBounds, Op.DIFFERENCE); } super.drawStrokeShape(canvas); canvas.restore(); @@ -106,8 +120,8 @@ private static class ImplApi14 extends CutoutDrawable { private Paint cutoutPaint; private int savedLayer; - ImplApi14(@Nullable ShapeAppearanceModel shapeAppearanceModel) { - super(shapeAppearanceModel); + ImplApi14(@NonNull CutoutDrawableState drawableState) { + super(drawableState); } @Override @@ -120,7 +134,7 @@ public void draw(@NonNull Canvas canvas) { @Override protected void drawStrokeShape(@NonNull Canvas canvas) { super.drawStrokeShape(canvas); - canvas.drawRect(cutoutBounds, getCutoutPaint()); + canvas.drawRect(drawableState.cutoutBounds, getCutoutPaint()); } private Paint getCutoutPaint() { @@ -168,4 +182,28 @@ private boolean useHardwareLayer(Callback callback) { return callback instanceof View; } } + + private static final class CutoutDrawableState extends MaterialShapeDrawableState { + @NonNull private final RectF cutoutBounds; + + private CutoutDrawableState( + @NonNull ShapeAppearanceModel shapeAppearanceModel, @NonNull RectF cutoutBounds) { + super(shapeAppearanceModel, null); + this.cutoutBounds = cutoutBounds; + } + + private CutoutDrawableState(@NonNull CutoutDrawableState state) { + super(state); + this.cutoutBounds = state.cutoutBounds; + } + + @NonNull + @Override + public Drawable newDrawable() { + CutoutDrawable drawable = CutoutDrawable.create(this); + // Force the calculation of the path for the new drawable. + drawable.invalidateSelf(); + return drawable; + } + } }