@@ -107,6 +107,7 @@ public class MaterialShapeDrawable extends Drawable implements TintAwareDrawable
107107 private final Path pathInsetByStroke = new Path ();
108108 private final PointF pointF = new PointF ();
109109 private final RectF rectF = new RectF ();
110+ private final RectF insetRectF = new RectF ();
110111 private final ShapePath shapePath = new ShapePath ();
111112 private final Region transparentRegion = new Region ();
112113 private final Region scratchRegion = new Region ();
@@ -115,6 +116,7 @@ public class MaterialShapeDrawable extends Drawable implements TintAwareDrawable
115116
116117 private ShapeAppearanceModel shapeAppearanceModel ;
117118 private int shadowCompatMode = SHADOW_COMPAT_MODE_DEFAULT ;
119+ private boolean paintShadowEnabled = false ;
118120 private boolean useTintColorForShadow = false ;
119121 private float interpolation = 1f ;
120122 private int shadowCompatElevation = 0 ;
@@ -465,6 +467,15 @@ public void setShadowEnabled(boolean shadowEnabled) {
465467 shadowEnabled ? SHADOW_COMPAT_MODE_DEFAULT : SHADOW_COMPAT_MODE_NEVER );
466468 }
467469
470+ /** TODO: Remove the paint shadow */
471+ public void setPaintShadowEnabled (boolean paintShadowEnabled ) {
472+ this .paintShadowEnabled = paintShadowEnabled ;
473+ shadowCompatMode = SHADOW_COMPAT_MODE_NEVER ;
474+ // Backwards compatible defaults.
475+ shadowCompatElevation = 5 ;
476+ shadowCompatRadius = 10 ;
477+ }
478+
468479 /**
469480 * Get the interpolation of the path, between 0 and 1. Ranges between 0 (none) and 1 (fully)
470481 * interpolated.
@@ -579,7 +590,7 @@ public void setShadowRadius(int shadowRadius) {
579590 * 21 or when the shape is concave.
580591 */
581592 private boolean requiresCompatShadow () {
582- return VERSION .SDK_INT < VERSION_CODES .LOLLIPOP || !pathInsetByStroke .isConvex ();
593+ return VERSION .SDK_INT < VERSION_CODES .LOLLIPOP || !path .isConvex ();
583594 }
584595
585596 /**
@@ -705,7 +716,11 @@ public void draw(Canvas canvas) {
705716 final int prevStrokeAlpha = strokePaint .getAlpha ();
706717 strokePaint .setAlpha (modulateAlpha (prevStrokeAlpha , alpha ));
707718
708- calculatePath (getBoundsInsetByStroke (), pathInsetByStroke );
719+ if (shadowCompatElevation > 0 && paintShadowEnabled ) {
720+ fillPaint .setShadowLayer (shadowCompatRadius , 0 , shadowCompatElevation , Color .BLACK );
721+ }
722+
723+ calculatePath (getBoundsAsRectF (), path );
709724 if (hasCompatShadow ()) {
710725 // Save the canvas before changing the clip bounds.
711726 canvas .save ();
@@ -746,22 +761,72 @@ public void draw(Canvas canvas) {
746761 strokePaint .setAlpha (prevStrokeAlpha );
747762 }
748763
749- private void drawStrokeShape (Canvas canvas ) {
750- drawShape (canvas , strokePaint );
764+ /** Draw the path or try to draw a round rect if possible. */
765+ private void drawShape (Canvas canvas , Paint paint , Path path , RectF bounds ) {
766+ if (shapeAppearanceModel .isRoundRect ()) {
767+ float cornerSize = shapeAppearanceModel .getTopRightCorner ().getCornerSize ();
768+ canvas .drawRoundRect (bounds , cornerSize , cornerSize , paint );
769+ } else {
770+ canvas .drawPath (path , paint );
771+ }
751772 }
752773
753774 private void drawFillShape (Canvas canvas ) {
754- drawShape (canvas , fillPaint );
775+ drawShape (canvas , fillPaint , path , getFillBounds () );
755776 }
756777
757- /** Draw the path or try to draw a round rect if possible. */
758- private void drawShape ( Canvas canvas , Paint paint ) {
759- if ( shapeAppearanceModel . isRoundRect ()) {
760- float cornerSize = shapeAppearanceModel . getTopRightCorner (). getCornerSize ();
761- canvas . drawRoundRect ( getBoundsInsetByStroke (), cornerSize , cornerSize , paint );
762- } else {
763- canvas . drawPath ( pathInsetByStroke , paint );
778+ private RectF getFillBounds () {
779+ RectF fillBounds = getBoundsAsRectF ();
780+ // If there's a stroke, inset the bounds by a hairline to prevent the fill from peeking out from
781+ // under the stroke.
782+ if ( hasStroke ()) {
783+ float hairline = 1f ;
784+ fillBounds . inset ( hairline , hairline );
764785 }
786+ return fillBounds ;
787+ }
788+
789+ private void drawStrokeShape (Canvas canvas ) {
790+ drawStrokeAdjustedForCornerSize (canvas );
791+ }
792+
793+ private void drawStrokeAdjustedForCornerSize (Canvas canvas ) {
794+ float cornerSizeTopLeft = getShapeAppearanceModel ().getTopLeftCorner ().cornerSize ;
795+ float cornerSizeTopRight = getShapeAppearanceModel ().getTopRightCorner ().cornerSize ;
796+ float cornerSizeBottomRight = getShapeAppearanceModel ().getBottomRightCorner ().cornerSize ;
797+ float cornerSizeBottomLeft = getShapeAppearanceModel ().getBottomLeftCorner ().cornerSize ;
798+
799+ // Adjust corner radius in order to draw the stroke so that the corners of the background are
800+ // drawn on top of the edges.
801+ setShapeAppearanceCornerSize (
802+ adjustCornerSizeForStrokeSize (cornerSizeTopLeft ),
803+ adjustCornerSizeForStrokeSize (cornerSizeTopRight ),
804+ adjustCornerSizeForStrokeSize (cornerSizeBottomRight ),
805+ adjustCornerSizeForStrokeSize (cornerSizeBottomLeft ));
806+
807+ RectF boundsInsetByStroke = getBoundsInsetByStroke ();
808+ calculatePath (boundsInsetByStroke , pathInsetByStroke );
809+ drawShape (canvas , strokePaint , pathInsetByStroke , boundsInsetByStroke );
810+
811+ // Set the corner radius back to its original size so that it draws on top of the fill.
812+ setShapeAppearanceCornerSize (
813+ cornerSizeTopLeft , cornerSizeTopRight , cornerSizeBottomRight , cornerSizeBottomLeft );
814+ }
815+
816+ private void setShapeAppearanceCornerSize (
817+ float cornerSizeTopLeft ,
818+ float cornerSizeTopRight ,
819+ float cornerSizeBottomRight ,
820+ float cornerSizeBottomLeft ) {
821+ shapeAppearanceModel .getTopLeftCorner ().setCornerSize (cornerSizeTopLeft );
822+ shapeAppearanceModel .getTopRightCorner ().setCornerSize (cornerSizeTopRight );
823+ shapeAppearanceModel .getBottomRightCorner ().setCornerSize (cornerSizeBottomRight );
824+ shapeAppearanceModel .getBottomLeftCorner ().setCornerSize (cornerSizeBottomLeft );
825+ }
826+
827+ private float adjustCornerSizeForStrokeSize (float cornerSize ) {
828+ float adjustedCornerSize = cornerSize - getStrokeInsetLength ();
829+ return Math .max (adjustedCornerSize , 0 );
765830 }
766831
767832 private void prepareCanvasForShadow (Canvas canvas ) {
@@ -791,7 +856,7 @@ private void prepareCanvasForShadow(Canvas canvas) {
791856 */
792857 private void drawCompatShadow (Canvas canvas ) {
793858 if (shadowCompatOffset != 0 ) {
794- canvas .drawPath (pathInsetByStroke , shadowRenderer .getShadowPaint ());
859+ canvas .drawPath (path , shadowRenderer .getShadowPaint ());
795860 }
796861
797862 // Draw the fake shadow for each of the corners and edges.
@@ -806,7 +871,7 @@ private void drawCompatShadow(Canvas canvas) {
806871 int shadowOffsetY = (int ) (shadowCompatOffset * Math .cos (Math .toRadians (shadowCompatRotation )));
807872
808873 canvas .translate (-shadowOffsetX , -shadowOffsetY );
809- canvas .drawPath (pathInsetByStroke , clearPaint );
874+ canvas .drawPath (path , clearPaint );
810875 canvas .translate (shadowOffsetX , shadowOffsetY );
811876 }
812877
@@ -1049,20 +1114,21 @@ private boolean updateColorsForState(int[] state, boolean invalidateSelf) {
10491114 return invalidateSelf ;
10501115 }
10511116
1052- private RectF getBoundsInsetByStroke () {
1053- RectF bounds = getBoundsAsRectF ();
1054- float strokeInsetWidth = getStrokeInsetWidth ();
1055- bounds .inset (strokeInsetWidth , strokeInsetWidth );
1056- return bounds ;
1057- }
1058-
1059- private float getStrokeInsetWidth () {
1117+ private float getStrokeInsetLength () {
10601118 if (hasStroke ()) {
10611119 return strokePaint .getStrokeWidth () / 2.0f ;
10621120 }
10631121 return 0f ;
10641122 }
10651123
1124+ private RectF getBoundsInsetByStroke () {
1125+ RectF rectF = getBoundsAsRectF ();
1126+ float inset = getStrokeInsetLength ();
1127+ insetRectF .set (
1128+ rectF .left + inset , rectF .top + inset , rectF .right - inset , rectF .bottom - inset );
1129+ return insetRectF ;
1130+ }
1131+
10661132 /**
10671133 * Dummy implementation of constant state. This drawable doesn't have shared state. Implementing
10681134 * so that calls to getConstantState().newDrawable() don't crash on L and M.
0 commit comments