@@ -17,12 +17,14 @@ package uk.gov.hmrc.components.molecule.donut
1717
1818import android.content.Context
1919import android.graphics.Canvas
20+ import android.graphics.DashPathEffect
2021import android.graphics.Paint
2122import android.graphics.RectF
2223import android.provider.Settings
2324import android.util.AttributeSet
2425import android.view.View
2526import android.view.animation.Animation
27+ import android.view.animation.DecelerateInterpolator
2628import android.view.animation.Transformation
2729import androidx.core.content.ContextCompat
2830import uk.gov.hmrc.components.R
@@ -38,12 +40,16 @@ class DonutChartView @JvmOverloads constructor(
3840 private val color2: Int
3941 private val color3: Int
4042
41- private val color1Paint: Paint
42- private val color2Paint: Paint
43- private val color3Paint: Paint
43+ private val stripes1: Boolean
44+ private val stripes2: Boolean
45+ private val stripes3: Boolean
46+
47+ private lateinit var color1Paint: Paint
48+ private lateinit var color2Paint: Paint
49+ private lateinit var color3Paint: Paint
4450
4551 private val donutWidth = context.resources.getDimension(R .dimen.donut_width)
46- private var rect: RectF ? = null
52+ private lateinit var rect: RectF
4753 private val startAngle: Float = LEFT_OF_CIRCLE
4854 private var value1SweepAngle: Float = 0f
4955 private var value2SweepAngle: Float = 0f
@@ -64,13 +70,29 @@ class DonutChartView @JvmOverloads constructor(
6470 R .styleable.DonutChartView_color3 ,
6571 ContextCompat .getColor(context, R .color.hmrc_donut_chart_color_3)
6672 )
73+ stripes1 = getBoolean(
74+ R .styleable.DonutChartView_stripes1 ,
75+ false
76+ )
77+ stripes2 = getBoolean(
78+ R .styleable.DonutChartView_stripes2 ,
79+ false
80+ )
81+ stripes3 = getBoolean(
82+ R .styleable.DonutChartView_stripes3 ,
83+ true
84+ )
6785 } finally {
6886 recycle()
6987 }
88+ }
89+ }
7090
71- color1Paint = donutPaintWithColour(color1)
72- color2Paint = donutPaintWithColour(color2)
73- color3Paint = donutPaintWithColour(color3)
91+ private fun stripesOrPlain (stripes : Boolean , color : Int ): Paint {
92+ return if (stripes) {
93+ donutPaintWithStripes(color)
94+ } else {
95+ donutPaintWithColour(color)
7496 }
7597 }
7698
@@ -80,6 +102,27 @@ class DonutChartView @JvmOverloads constructor(
80102 color = paintColor
81103 }
82104
105+ private fun donutPaintWithStripes (paintColor1 : Int ): Paint {
106+ val width = rect.width()
107+ val height = rect.height()
108+ val diameter = min(width, height)
109+ val radius = diameter / 2F
110+
111+ val circumference: Float = (2F * Math .PI * radius).toFloat()
112+ val dashPlusGapSize = (circumference / TOTAL_NUM_OF_DASHES )
113+ val intervals = floatArrayOf(dashPlusGapSize * INTERVAL_GAP , dashPlusGapSize * INTERVAL_GAP )
114+
115+ val path = DashPathEffect (intervals, 0F )
116+
117+ val paint2 = Paint (Paint .ANTI_ALIAS_FLAG ).apply {
118+ style = Paint .Style .STROKE
119+ strokeWidth = donutWidth
120+ color = paintColor1
121+ pathEffect = path
122+ }
123+ return paint2
124+ }
125+
83126 override fun onMeasure (widthMeasureSpec : Int , heightMeasureSpec : Int ) {
84127 val w = resolveSizeAndState(suggestedMinimumWidth, widthMeasureSpec, 1 )
85128 val h = resolveSizeAndState(MeasureSpec .getSize(w), heightMeasureSpec, 0 )
@@ -101,10 +144,35 @@ class DonutChartView @JvmOverloads constructor(
101144 }
102145
103146 override fun onDraw (canvas : Canvas ? ) {
147+ val white = stripesOrPlain(false , ContextCompat .getColor(context, R .color.hmrc_white_background))
148+
104149 canvas?.apply {
105- drawArc(rect!! , startAngle, value1SweepAngle, false , color1Paint)
106- drawArc(rect!! , startAngle, value2SweepAngle, false , color2Paint)
107- drawArc(rect!! , startAngle, value3SweepAngle, false , color3Paint)
150+ if (value1SweepAngle - startAngle < DEGREES_FOR_NO_STRIPES ) {
151+ color1Paint = donutPaintWithColour(color1)
152+ drawArc(rect, startAngle, value1SweepAngle, false , color1Paint)
153+ } else {
154+ color1Paint = stripesOrPlain(stripes1, color1)
155+ drawArc(rect, startAngle, value1SweepAngle, false , white)
156+ drawArc(rect, startAngle, value1SweepAngle, false , color1Paint)
157+ }
158+
159+ if (0f - value2SweepAngle < DEGREES_FOR_NO_STRIPES ) {
160+ color2Paint = donutPaintWithColour(color2)
161+ drawArc(rect, startAngle, value2SweepAngle, false , color2Paint)
162+ } else {
163+ color2Paint = stripesOrPlain(stripes2, color2)
164+ drawArc(rect, startAngle, value2SweepAngle, false , white)
165+ drawArc(rect, startAngle, value2SweepAngle, false , color2Paint)
166+ }
167+
168+ if (0f - value3SweepAngle < DEGREES_FOR_NO_STRIPES ) {
169+ color3Paint = donutPaintWithColour(ContextCompat .getColor(context, R .color.hmrc_pink))
170+ drawArc(rect, startAngle, value3SweepAngle, false , color3Paint)
171+ } else {
172+ color3Paint = stripesOrPlain(stripes3, color3)
173+ drawArc(rect, startAngle, value3SweepAngle, false , white)
174+ drawArc(rect, startAngle, value3SweepAngle, false , color3Paint)
175+ }
108176 }
109177 }
110178
@@ -137,14 +205,17 @@ class DonutChartView @JvmOverloads constructor(
137205 setOnAnimationEnd {
138206 Value3ArcAnimation (value3Spread).apply {
139207 duration = (value3Duration * animationScale).toLong()
208+ interpolator = DecelerateInterpolator ()
140209 startAnimation(this )
141210 }
142211 }
143212 duration = (value2Duration * animationScale).toLong()
213+ interpolator = DecelerateInterpolator ()
144214 startAnimation(this )
145215 }
146216 }
147217 duration = (value1Duration * animationScale).toLong()
218+ interpolator = DecelerateInterpolator ()
148219 startAnimation(this )
149220 }
150221 }
@@ -182,13 +253,16 @@ class DonutChartView @JvmOverloads constructor(
182253 }
183254
184255 companion object {
185- private const val VALUE_1_ANIMATION_DURATION = 600L
186- private const val VALUE_2_ANIMATION_DURATION = 300L
187- private const val VALUE_3_ANIMATION_DURATION = 300L
256+ private const val VALUE_1_ANIMATION_DURATION = 1200L
257+ private const val VALUE_2_ANIMATION_DURATION = 600L
258+ private const val VALUE_3_ANIMATION_DURATION = 600L
188259 private const val NO_ANIMATION_DURATION = 0L
189260 private const val LEFT_OF_CIRCLE = 270f
190261 private const val ONE_HUNDRED_PERCENT = 100f
191262 private const val FULL_CIRCLE_ANGLE_IN_WHOLE_NUMBER = 360
192263 private const val FULL_CIRCLE_ANGLE_IN_DECIMALS_NUMBER = 3.6
264+ private const val TOTAL_NUM_OF_DASHES = 80f
265+ private const val INTERVAL_GAP = 0.5f
266+ private const val DEGREES_FOR_NO_STRIPES = 18f
193267 }
194268}
0 commit comments