11/*
2- * Copyright (C) 2015 The Android Open Source Project
2+ * Copyright (C) 2018 The Android Open Source Project
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1414 * limitations under the License.
1515 */
1616
17- package com .google .android .material .internal ;
17+ package com .google .android .material .floatingactionbutton ;
1818
1919import static android .support .annotation .RestrictTo .Scope .LIBRARY_GROUP ;
2020
21+ import android .annotation .TargetApi ;
2122import android .content .res .ColorStateList ;
2223import android .graphics .Canvas ;
2324import android .graphics .ColorFilter ;
2425import android .graphics .LinearGradient ;
26+ import android .graphics .Outline ;
2527import android .graphics .Paint ;
28+ import android .graphics .Path ;
2629import android .graphics .PixelFormat ;
2730import android .graphics .Rect ;
2831import android .graphics .RectF ;
2932import android .graphics .Shader ;
3033import android .graphics .drawable .Drawable ;
34+ import android .os .Build .VERSION_CODES ;
3135import android .support .annotation .ColorInt ;
3236import android .support .annotation .Dimension ;
33- import android .support .annotation .FloatRange ;
3437import android .support .annotation .IntRange ;
3538import android .support .annotation .NonNull ;
36- import android .support .annotation .Nullable ;
3739import android .support .annotation .RestrictTo ;
40+ import com .google .android .material .shape .ShapeAppearanceModel ;
41+ import com .google .android .material .shape .ShapeAppearancePathProvider ;
3842import android .support .v4 .graphics .ColorUtils ;
3943
40- /** A drawable which draws an oval 'border'. */
44+ /**
45+ * A Drawable that draws borders for {@link FloatingActionButton}
46+ *
47+ * @hide
48+ * */
4149@ RestrictTo (LIBRARY_GROUP )
42- public class CircularBorderDrawable extends Drawable {
50+ class BorderDrawable extends Drawable {
4351
4452 /**
4553 * We actually draw the stroke wider than the border size given. This is to reduce any potential
4654 * transparent space caused by anti-aliasing and padding rounding. This value defines the
4755 * multiplier used to determine to draw stroke width.
4856 */
4957 private static final float DRAW_STROKE_WIDTH_MULTIPLE = 1.3333f ;
58+ private final ShapeAppearancePathProvider pathProvider = new ShapeAppearancePathProvider ();
5059
51- final Paint paint ;
52- final Rect rect = new Rect ();
53- final RectF rectF = new RectF ();
54- final CircularBorderState state = new CircularBorderState ();
60+ private final Paint paint ;
61+ private final Path shapePath = new Path ();
62+ private final Rect rect = new Rect ();
63+ private final RectF rectF = new RectF ();
5564
5665 @ Dimension float borderWidth ;
57-
5866 @ ColorInt private int topOuterStrokeColor ;
5967 @ ColorInt private int topInnerStrokeColor ;
6068 @ ColorInt private int bottomOuterStrokeColor ;
6169 @ ColorInt private int bottomInnerStrokeColor ;
62-
63- private ColorStateList borderTint ;
6470 @ ColorInt private int currentBorderTintColor ;
6571
6672 private boolean invalidateShader = true ;
73+ private ColorStateList borderTint ;
74+ private ShapeAppearanceModel shapeAppearanceModel ;
6775
68- @ FloatRange (from = 0 , to = 360 )
69- private float rotation ;
70-
71- public CircularBorderDrawable () {
76+ BorderDrawable (ShapeAppearanceModel shapeAppearanceModel ) {
77+ this .shapeAppearanceModel = shapeAppearanceModel ;
7278 paint = new Paint (Paint .ANTI_ALIAS_FLAG );
7379 paint .setStyle (Paint .Style .STROKE );
7480 }
7581
76- @ Nullable
82+ public void setBorderWidth (@ Dimension float width ) {
83+ if (borderWidth != width ) {
84+ borderWidth = width ;
85+ paint .setStrokeWidth (width * DRAW_STROKE_WIDTH_MULTIPLE );
86+ invalidateShader = true ;
87+ invalidateSelf ();
88+ }
89+ }
90+
91+ void setBorderTint (ColorStateList tint ) {
92+ if (tint != null ) {
93+ currentBorderTintColor = tint .getColorForState (getState (), currentBorderTintColor );
94+ }
95+ borderTint = tint ;
96+ invalidateShader = true ;
97+ invalidateSelf ();
98+ }
99+
77100 @ Override
78- public ConstantState getConstantState () {
79- return state ;
101+ public void setColorFilter (ColorFilter colorFilter ) {
102+ paint .setColorFilter (colorFilter );
103+ invalidateSelf ();
80104 }
81105
82- public void setGradientColors (
106+ void setGradientColors (
83107 @ ColorInt int topOuterStrokeColor ,
84108 @ ColorInt int topInnerStrokeColor ,
85109 @ ColorInt int bottomOuterStrokeColor ,
@@ -90,16 +114,6 @@ public void setGradientColors(
90114 this .bottomInnerStrokeColor = bottomInnerStrokeColor ;
91115 }
92116
93- /** Set the border width */
94- public void setBorderWidth (@ Dimension float width ) {
95- if (borderWidth != width ) {
96- borderWidth = width ;
97- paint .setStrokeWidth (width * DRAW_STROKE_WIDTH_MULTIPLE );
98- invalidateShader = true ;
99- invalidateSelf ();
100- }
101- }
102-
103117 @ Override
104118 public void draw (Canvas canvas ) {
105119 if (invalidateShader ) {
@@ -108,49 +122,59 @@ public void draw(Canvas canvas) {
108122 }
109123
110124 final float halfBorderWidth = paint .getStrokeWidth () / 2f ;
111- final RectF rectF = this .rectF ;
125+ copyBounds (rect );
126+ rectF .set (rect );
112127
113128 // We need to inset the oval bounds by half the border width. This is because stroke draws
114129 // the center of the border on the dimension. Whereas we want the stroke on the inside.
130+ if (shapeAppearanceModel .isRoundRect ()) {
131+ rectF .inset (halfBorderWidth , halfBorderWidth );
132+ canvas .drawRoundRect (rectF , rectF .width () / 2f , rectF .height () / 2f , paint );
133+ return ;
134+ }
135+
136+ pathProvider .calculatePath (shapeAppearanceModel , 1f , rectF , shapePath );
137+ canvas .drawPath (shapePath , paint );
138+ }
139+
140+ @ TargetApi (VERSION_CODES .LOLLIPOP )
141+ @ Override
142+ public void getOutline (@ NonNull Outline outline ) {
143+ if (shapeAppearanceModel .isRoundRect ()) {
144+ float radius = shapeAppearanceModel .getTopLeftCorner ().getCornerSize ();
145+ outline .setRoundRect (getBounds (), radius );
146+ return ;
147+ }
148+
115149 copyBounds (rect );
116150 rectF .set (rect );
117- rectF .left += halfBorderWidth ;
118- rectF .top += halfBorderWidth ;
119- rectF .right -= halfBorderWidth ;
120- rectF .bottom -= halfBorderWidth ;
121-
122- canvas .save ();
123- canvas .rotate (rotation , rectF .centerX (), rectF .centerY ());
124- // Draw the oval
125- canvas .drawOval (rectF , paint );
126- canvas .restore ();
151+ pathProvider .calculatePath (shapeAppearanceModel , 1f , rectF , shapePath );
152+ if (shapePath .isConvex ()) {
153+ outline .setConvexPath (shapePath );
154+ }
127155 }
128156
129157 @ Override
130158 public boolean getPadding (Rect padding ) {
131- final int borderWidth = Math .round (this .borderWidth );
132- padding .set (borderWidth , borderWidth , borderWidth , borderWidth );
159+ if (shapeAppearanceModel .isRoundRect ()) {
160+ final int borderWidth = Math .round (this .borderWidth );
161+ padding .set (borderWidth , borderWidth , borderWidth , borderWidth );
162+ }
133163 return true ;
134164 }
135165
136- @ Override
137- public void setAlpha (@ IntRange (from = 0 , to = 255 ) int alpha ) {
138- paint .setAlpha (alpha );
139- invalidateSelf ();
166+ public ShapeAppearanceModel getShapeAppearanceModel () {
167+ return shapeAppearanceModel ;
140168 }
141169
142- public void setBorderTint (ColorStateList tint ) {
143- if (tint != null ) {
144- currentBorderTintColor = tint .getColorForState (getState (), currentBorderTintColor );
145- }
146- borderTint = tint ;
147- invalidateShader = true ;
148- invalidateSelf ();
170+ public void setShapeAppearanceModel (
171+ ShapeAppearanceModel shapeAppearanceModel ) {
172+ this .shapeAppearanceModel = shapeAppearanceModel ;
149173 }
150174
151175 @ Override
152- public void setColorFilter ( ColorFilter colorFilter ) {
153- paint .setColorFilter ( colorFilter );
176+ public void setAlpha ( @ IntRange ( from = 0 , to = 255 ) int alpha ) {
177+ paint .setAlpha ( alpha );
154178 invalidateSelf ();
155179 }
156180
@@ -159,13 +183,6 @@ public int getOpacity() {
159183 return borderWidth > 0 ? PixelFormat .TRANSLUCENT : PixelFormat .TRANSPARENT ;
160184 }
161185
162- public final void setRotation (float rotation ) {
163- if (rotation != this .rotation ) {
164- this .rotation = rotation ;
165- invalidateSelf ();
166- }
167- }
168-
169186 @ Override
170187 protected void onBoundsChange (Rect bounds ) {
171188 invalidateShader = true ;
@@ -191,11 +208,6 @@ protected boolean onStateChange(int[] state) {
191208 return invalidateShader ;
192209 }
193210
194- /**
195- * Creates a vertical {@link LinearGradient}
196- *
197- * @return
198- */
199211 private Shader createGradientShader () {
200212 final Rect rect = this .rect ;
201213 copyBounds (rect );
@@ -225,22 +237,4 @@ private Shader createGradientShader() {
225237 return new LinearGradient (
226238 0 , rect .top , 0 , rect .bottom , colors , positions , Shader .TileMode .CLAMP );
227239 }
228-
229- /**
230- * Dummy implementation of constant state. This drawable doesn't have shared state. Implementing
231- * so that calls to getConstantState().newDrawable() don't crash on L and M.
232- */
233- private class CircularBorderState extends ConstantState {
234-
235- @ NonNull
236- @ Override
237- public Drawable newDrawable () {
238- return CircularBorderDrawable .this ;
239- }
240-
241- @ Override
242- public int getChangingConfigurations () {
243- return 0 ;
244- }
245- }
246240}
0 commit comments