@@ -6,14 +6,22 @@ import android.content.res.ColorStateList
6
6
import android.content.res.Resources
7
7
import android.content.res.TypedArray
8
8
import android.graphics.Color
9
+ import android.graphics.LinearGradient
10
+ import android.graphics.Paint
9
11
import android.graphics.Path
12
+ import android.graphics.RadialGradient
10
13
import android.graphics.Rect
11
14
import android.graphics.RectF
15
+ import android.graphics.Shader
16
+ import android.graphics.SweepGradient
17
+ import android.graphics.drawable.GradientDrawable
12
18
import android.util.AttributeSet
13
19
import androidx.core.content.res.TypedArrayUtils.obtainAttributes
14
20
import androidx.core.graphics.ColorUtils
21
+ import androidx.core.graphics.toRectF
15
22
import com.google.android.material.shadow.ShadowRenderer
16
23
import com.google.android.material.shape.CornerSize
24
+ import com.google.android.material.shape.CornerTreatment
17
25
import com.google.android.material.shape.CutCornerTreatment
18
26
import com.google.android.material.shape.MaterialShapeDrawable
19
27
import com.google.android.material.shape.RoundedCornerTreatment
@@ -38,6 +46,7 @@ class ShapeableDrawable(shapeModel: ShapeAppearanceModel) : MaterialShapeDrawabl
38
46
val clipPath = Path ()
39
47
40
48
private val clipRect: RectF = RectF ()
49
+ private val mGradientState = GradientState ()
41
50
42
51
constructor () : this (ShapeAppearanceModel ())
43
52
@@ -57,53 +66,121 @@ class ShapeableDrawable(shapeModel: ShapeAppearanceModel) : MaterialShapeDrawabl
57
66
ta.recycle()
58
67
}
59
68
60
- private fun initAttrs (ta : TypedArray ) {
61
- strokeColor = ta .getColorStateList(R .styleable.ShapeableDrawable_strokeColor )
62
- strokeWidth = ta .getDimensionPixelSize(R .styleable.ShapeableDrawable_strokeWidth , 0 ).toFloat()
63
- tintList = ta .getColorStateList(R .styleable.ShapeableDrawable_backgroundTint ) ? : ColorStateList .valueOf(Color .TRANSPARENT )
69
+ private fun initAttrs (a : TypedArray ) {
70
+ strokeColor = a .getColorStateList(R .styleable.ShapeableDrawable_strokeColor )
71
+ strokeWidth = a .getDimensionPixelSize(R .styleable.ShapeableDrawable_strokeWidth , 0 ).toFloat()
72
+ tintList = a .getColorStateList(R .styleable.ShapeableDrawable_backgroundTint ) ? : ColorStateList .valueOf(Color .TRANSPARENT )
64
73
65
- if (ta.hasValue(R .styleable.ShapeableDrawable_cornerSize )) {
66
- setCornerSize(ta.getDimensionPixelSize(R .styleable.ShapeableDrawable_cornerSize , 0 ).toFloat())
74
+ val cornerSize = a.getDimension(R .styleable.ShapeableDrawable_cornerSize , 0f )
75
+ if (cornerSize > 0f ) {
76
+ val cornerType = a.getInteger(R .styleable.ShapeableDrawable_cornerType , 0 )
77
+ val cornerPosition = a.getInteger(R .styleable.ShapeableDrawable_cornerPosition , 0 )
78
+ setCorners(cornerSize, cornerPosition, cornerType)
67
79
}
68
80
69
- if (ta.hasValue(R .styleable.ShapeableDrawable_cornerType )) {
70
- val cornerTreatment = when (ta.getInteger(R .styleable.ShapeableDrawable_cornerType , 0 )) {
71
- 0 -> RoundedCornerTreatment ()
72
- 1 -> CutCornerTreatment ()
73
- 2 -> ConcaveCornerTreatment ()
74
- else -> RoundedCornerTreatment ()
75
- }
76
- shapeAppearanceModel = shapeAppearanceModel.toBuilder().setAllCorners(cornerTreatment).build()
81
+ // 渐变背景色
82
+ mGradientState.gradientColors = a.getGradientColors()
83
+ mGradientState.gradientType = a.getInt(R .styleable.ShapeableDrawable_gradientType , GradientDrawable .LINEAR_GRADIENT )
84
+ mGradientState.gradientRadius = a.getDimension(R .styleable.ShapeableDrawable_gradientRadius , 0f )
85
+ mGradientState.gradientCenterX = a.getFloat(R .styleable.ShapeableDrawable_gradientCenterX , 0.5f )
86
+ mGradientState.gradientCenterY = a.getFloat(R .styleable.ShapeableDrawable_gradientCenterY , 0.5f )
87
+ mGradientState.gradientOrientation = try {
88
+ GradientDrawable .Orientation .values()[a.getInt(R .styleable.ShapeableDrawable_gradientOrientation , 0 )]
89
+ } catch (e: IndexOutOfBoundsException ) {
90
+ GradientDrawable .Orientation .TOP_BOTTOM
77
91
}
78
92
79
- if (ta.hasValue(R .styleable.ShapeableDrawable_shadowRadius )) {
80
- shadowRadius = ta.getDimensionPixelSize(R .styleable.ShapeableDrawable_shadowRadius , 0 )
81
- }
82
- if (ta.hasValue(R .styleable.ShapeableDrawable_shadowColor )) {
83
- setShadowColor(ta.getColor(R .styleable.ShapeableDrawable_shadowColor , 0 ))
93
+ if (mGradientState.gradientColors != null ) {
94
+ setUseTintColorForShadow(false )
95
+ tintList = null
84
96
}
85
97
86
-
87
- val shadowOffsetY = ta.getDimensionPixelSize(R .styleable.ShapeableDrawable_shadowOffsetY , 0 )
88
- val shadowOffsetX = ta.getDimensionPixelSize(R .styleable.ShapeableDrawable_shadowOffsetX , 0 )
89
- setShadowOffset(shadowOffsetX, shadowOffsetY)
98
+ // 阴影
99
+ if (a.hasValue(R .styleable.ShapeableDrawable_shadowRadius )) {
100
+ shadowRadius = a.getDimensionPixelSize(R .styleable.ShapeableDrawable_shadowRadius , 0 )
101
+ }
102
+ if (a.hasValue(R .styleable.ShapeableDrawable_shadowColor )) {
103
+ setShadowColor(a.getColor(R .styleable.ShapeableDrawable_shadowColor , 0 ))
104
+ }
105
+ if (a.hasValue(R .styleable.ShapeableDrawable_shadowOffsetX ) || a.hasValue(R .styleable.ShapeableDrawable_shadowOffsetY )) {
106
+ val shadowOffsetY = a.getDimensionPixelSize(R .styleable.ShapeableDrawable_shadowOffsetY , 0 )
107
+ val shadowOffsetX = a.getDimensionPixelSize(R .styleable.ShapeableDrawable_shadowOffsetX , 0 )
108
+ setShadowOffset(shadowOffsetX, shadowOffsetY)
109
+ }
90
110
91
111
92
- val arrowSize = ta.getDimension(R .styleable.ShapeableDrawable_arrowSize , 0f )
112
+ // 汽泡箭头
113
+ val arrowSize = a.getDimension(R .styleable.ShapeableDrawable_arrowSize , 0f )
93
114
if (arrowSize > 0f ) {
94
- val arrowOffset = ta .getDimension(R .styleable.ShapeableDrawable_arrowOffset , 0f )
95
- val arrowEdge = ta .getInteger(R .styleable.ShapeableDrawable_arrowEdge , ArrowEdgeTreatment .EDGE_BOTTOM )
96
- val arrowAlign = ta .getInteger(R .styleable.ShapeableDrawable_arrowAlign , ArrowEdgeTreatment .ALIGN_CENTER )
115
+ val arrowOffset = a .getDimension(R .styleable.ShapeableDrawable_arrowOffset , 0f )
116
+ val arrowEdge = a .getInteger(R .styleable.ShapeableDrawable_arrowEdge , ArrowEdgeTreatment .EDGE_BOTTOM )
117
+ val arrowAlign = a .getInteger(R .styleable.ShapeableDrawable_arrowAlign , ArrowEdgeTreatment .ALIGN_CENTER )
97
118
setArrow(arrowSize, arrowOffset, arrowEdge, arrowAlign)
98
119
}
99
120
}
100
121
122
+ private fun TypedArray.getGradientColors (): IntArray? {
123
+
124
+ val hasStartColor = hasValue(R .styleable.ShapeableDrawable_gradientStartColor )
125
+ val hasCenterColor = hasValue(R .styleable.ShapeableDrawable_gradientCenterColor )
126
+ val hasEndColor = hasValue(R .styleable.ShapeableDrawable_gradientEndColor )
127
+
128
+ if (hasStartColor || hasCenterColor || hasEndColor) {
129
+ val start = getColor(R .styleable.ShapeableDrawable_gradientStartColor , 0 )
130
+ val center = getColor(R .styleable.ShapeableDrawable_gradientCenterColor , 0 )
131
+ val end = getColor(R .styleable.ShapeableDrawable_gradientEndColor , 0 )
132
+ return if (hasCenterColor) intArrayOf(start, center, end) else intArrayOf(start, end)
133
+ }
134
+ return null
135
+ }
136
+
101
137
@Suppress(" OVERRIDE_DEPRECATION" )
102
138
override fun setShadowRadius (radius : Int ) {
103
139
@Suppress(" DEPRECATION" )
104
140
super .setShadowRadius(radius)
105
141
}
106
142
143
+ private fun setCorners (size : Float , position : Int , type : Int ) {
144
+ val corner = when (type) {
145
+ 0 -> RoundedCornerTreatment ()
146
+ 1 -> CutCornerTreatment ()
147
+ 2 -> ConcaveCornerTreatment ()
148
+ else -> RoundedCornerTreatment ()
149
+ }
150
+ val builder = shapeAppearanceModel.toBuilder()
151
+ shapeAppearanceModel = when (position) {
152
+ 1 -> builder.tl(corner, size).build()
153
+ 2 -> builder.tr(corner, size).build()
154
+ 3 -> builder.bl(corner, size).build()
155
+ 4 -> builder.br(corner, size).build()
156
+
157
+ 5 -> builder.tl(corner, size).tr(corner, size).build()
158
+ 6 -> builder.bl(corner, size).br(corner, size).build()
159
+ 7 -> builder.tl(corner, size).bl(corner, size).build()
160
+ 8 -> builder.tr(corner, size).br(corner, size).build()
161
+
162
+ 9 -> builder.tl(corner, size).br(corner, size).build()
163
+ 10 -> builder.bl(corner, size).tr(corner, size).build()
164
+
165
+ 11 -> builder.tr(corner, size).bl(corner, size).br(corner, size).build()
166
+ 12 -> builder.tl(corner, size).bl(corner, size).br(corner, size).build()
167
+ 13 -> builder.tl(corner, size).tr(corner, size).br(corner, size).build()
168
+ 14 -> builder.tl(corner, size).tr(corner, size).bl(corner, size).build()
169
+
170
+ else -> builder.setAllCorners(corner).setAllCornerSizes(size).build()
171
+ }
172
+
173
+ }
174
+
175
+ private inline fun ShapeAppearanceModel.Builder.tl (cornerTreatment : CornerTreatment , size : Float ) =
176
+ setTopLeftCorner(cornerTreatment).setTopLeftCornerSize(size)
177
+ private inline fun ShapeAppearanceModel.Builder.tr (cornerTreatment : CornerTreatment , size : Float ) =
178
+ setTopRightCorner(cornerTreatment).setTopRightCornerSize(size)
179
+ private inline fun ShapeAppearanceModel.Builder.bl (cornerTreatment : CornerTreatment , size : Float ) =
180
+ setBottomLeftCorner(cornerTreatment).setBottomLeftCornerSize(size)
181
+ private inline fun ShapeAppearanceModel.Builder.br (cornerTreatment : CornerTreatment , size : Float ) =
182
+ setBottomRightCorner(cornerTreatment).setBottomRightCornerSize(size)
183
+
107
184
fun setShadowOffset (offsetX : Int , offsetY : Int ) {
108
185
if (offsetX != 0 ) {
109
186
shadowCompatRotation = (atan(offsetY / offsetX.toFloat()) * 180 / PI ).toInt()
@@ -137,6 +214,11 @@ class ShapeableDrawable(shapeModel: ShapeAppearanceModel) : MaterialShapeDrawabl
137
214
override fun onBoundsChange (bounds : Rect ) {
138
215
super .onBoundsChange(bounds)
139
216
updateClipPath(bounds.width(), bounds.height())
217
+
218
+ mGradientState.createFillShader(bounds.toRectF()) ?.let {
219
+ (fieldFillPaint.get(this ) as Paint ).shader = it
220
+ }
221
+ invalidateSelf()
140
222
}
141
223
142
224
@@ -152,6 +234,49 @@ class ShapeableDrawable(shapeModel: ShapeAppearanceModel) : MaterialShapeDrawabl
152
234
clipPathProvider.calculatePath(newModel, 1f , clipRect, clipPath)
153
235
}
154
236
237
+ private class GradientState {
238
+
239
+ var gradientColors: IntArray? = null
240
+ var gradientType: Int = GradientDrawable .LINEAR_GRADIENT
241
+ var gradientOrientation: GradientDrawable .Orientation = GradientDrawable .Orientation .TL_BR
242
+ var gradientCenterX: Float = 0.5f
243
+ var gradientCenterY: Float = 0.5f
244
+ var gradientRadius: Float = 0.5f
245
+
246
+ fun createFillShader (rect : RectF ): Shader ? = when {
247
+ gradientColors == null -> null
248
+ gradientType == GradientDrawable .LINEAR_GRADIENT -> {
249
+ val r = when (gradientOrientation) {
250
+ GradientDrawable .Orientation .TOP_BOTTOM -> RectF (rect.left, rect.top, rect.left, rect.bottom)
251
+ GradientDrawable .Orientation .TR_BL -> RectF (rect.right, rect.top, rect.left, rect.bottom)
252
+ GradientDrawable .Orientation .RIGHT_LEFT -> RectF (rect.right, rect.top, rect.left, rect.top)
253
+ GradientDrawable .Orientation .BR_TL -> RectF (rect.right, rect.bottom, rect.left, rect.top)
254
+ GradientDrawable .Orientation .BOTTOM_TOP -> RectF (rect.left, rect.bottom, rect.left, rect.top)
255
+ GradientDrawable .Orientation .BL_TR -> RectF (rect.left, rect.bottom, rect.right, rect.top)
256
+ GradientDrawable .Orientation .LEFT_RIGHT -> RectF (rect.left, rect.top, rect.right, rect.top)
257
+ else -> RectF (rect.left, rect.top, rect.right, rect.bottom)
258
+ }
259
+ LinearGradient (r.left, r.top, r.right, r.bottom, gradientColors!! , null , Shader .TileMode .CLAMP )
260
+ }
261
+ gradientType == GradientDrawable .RADIAL_GRADIENT -> {
262
+ if (gradientRadius > 0 ) {
263
+ val x0 = rect.left + (rect.right - rect.left) * gradientCenterX
264
+ val y0 = rect.top + (rect.bottom - rect.top) * gradientCenterY
265
+ RadialGradient (x0, y0, gradientRadius, gradientColors!! , null , Shader .TileMode .CLAMP )
266
+ } else {
267
+ null
268
+ }
269
+ }
270
+ gradientType == GradientDrawable .SWEEP_GRADIENT -> {
271
+ val x0 = rect.left + (rect.right - rect.left) * gradientCenterX
272
+ val y0 = rect.top + (rect.bottom - rect.top) * gradientCenterY
273
+ SweepGradient (x0, y0, gradientColors!! , null )
274
+ }
275
+ else -> null
276
+ }
277
+
278
+ }
279
+
155
280
private class RealShadowRenderer : ShadowRenderer () {
156
281
override fun setShadowColor (shadowColor : Int ) {
157
282
val alpha = min(Color .alpha(shadowColor), 0x44 )
@@ -172,6 +297,7 @@ class ShapeableDrawable(shapeModel: ShapeAppearanceModel) : MaterialShapeDrawabl
172
297
private val clipPathProvider = ShapeAppearancePathProvider ()
173
298
174
299
private val fieldShadowRenderer by field(MaterialShapeDrawable ::class .java, " shadowRenderer" )
300
+ private val fieldFillPaint by field(MaterialShapeDrawable ::class .java, " fillPaint" )
175
301
176
302
private val fieldShadowStartColor by field(ShadowRenderer ::class .java, " shadowStartColor" )
177
303
private val fieldShadowMiddleColor by field(ShadowRenderer ::class .java, " shadowMiddleColor" )
0 commit comments