@@ -4,7 +4,7 @@ package com.faskn.lib
4
4
* Created by Furkan on 6.08.2020
5
5
*/
6
6
7
- import android.annotation.SuppressLint
7
+ import android.animation.ValueAnimator
8
8
import android.content.Context
9
9
import android.content.res.ColorStateList
10
10
import android.graphics.Canvas
@@ -16,6 +16,7 @@ import android.view.Gravity
16
16
import android.view.LayoutInflater
17
17
import android.view.MotionEvent
18
18
import android.view.View
19
+ import android.view.animation.LinearInterpolator
19
20
import android.widget.*
20
21
import androidx.core.content.ContextCompat
21
22
import androidx.core.view.doOnPreDraw
@@ -27,34 +28,43 @@ import kotlin.math.sin
27
28
28
29
class ClickablePieChart @JvmOverloads constructor(
29
30
context : Context ,
30
- attrs : AttributeSet ? = null ,
31
+ private val attrs : AttributeSet ? = null ,
31
32
defStyleAttr : Int = 0
32
33
) : View(context, attrs, defStyleAttr) {
33
34
34
35
private var slicePaint: Paint = Paint ()
35
36
private var centerPaint: Paint = Paint (Paint .ANTI_ALIAS_FLAG )
36
- private var sliceColors: IntArray = intArrayOf(Color .RED , Color .GREEN , Color .BLUE , Color .YELLOW )
37
37
private var rectF: RectF ? = null
38
- private var dataPoints: FloatArray = floatArrayOf()
39
- private var sliceStartPoint = 0F
38
+ private var sliceStartPoint = 0F // FIXME: 16-Aug-20 remove if unnecessary
40
39
private var sliceWidth = 80f
41
40
private var touchX = 0f
42
41
private var touchY = 0f
43
- private var clickListener: ((String , Float ) -> Unit )? = null
44
- private var pointsArray = arrayListOf<Pair <Float , Float >>()
42
+
43
+ // PieChart variables
44
+ private var pieChart: PieChart ? = null
45
+ private lateinit var slices: List <Slice >
46
+
47
+ // Animation variables
48
+ private var animator: ValueAnimator ? = null
49
+ private var currentSweepAngle = 0
45
50
46
51
// Attributes
47
52
private lateinit var popupText: String
48
53
49
54
init {
55
+ initAttributes(attrs)
56
+ }
57
+
58
+ private fun init () {
50
59
slicePaint.isAntiAlias = true
51
60
slicePaint.isDither = true
52
61
slicePaint.style = Paint .Style .FILL
53
62
54
63
centerPaint.color = Color .WHITE
55
64
centerPaint.style = Paint .Style .FILL
56
65
57
- initAttributes(attrs)
66
+ initSlices()
67
+ startAnimation()
58
68
}
59
69
60
70
private fun initAttributes (attrs : AttributeSet ? ) {
@@ -68,43 +78,76 @@ class ClickablePieChart @JvmOverloads constructor(
68
78
}
69
79
}
70
80
71
- private fun scale (): FloatArray {
72
- val scaledValues = FloatArray (dataPoints.size)
73
- for (i in dataPoints.indices) {
74
- scaledValues.fill((dataPoints[i] / getTotal()) * 360 , i, dataPoints.size)
81
+ private fun initSlices () {
82
+ slices = pieChart?.slices?.toList()!!
83
+ }
84
+
85
+ private fun startAnimation () {
86
+ animator?.cancel()
87
+ animator = ValueAnimator .ofInt(0 , 360 ).apply {
88
+ duration = 1000
89
+ interpolator = LinearInterpolator ()
90
+ addUpdateListener { valueAnimator ->
91
+ currentSweepAngle = valueAnimator.animatedValue as Int
92
+ invalidate()
93
+ }
75
94
}
76
- return scaledValues
95
+ animator?.start()
77
96
}
78
97
79
- @SuppressLint(" DrawAllocation" )
80
- override fun onDraw (canvas : Canvas ? ) {
81
- super .onDraw(canvas)
98
+ override fun onSizeChanged (w : Int , h : Int , oldw : Int , oldh : Int ) {
99
+ super .onSizeChanged(w, h, oldw, oldh)
82
100
83
101
rectF = RectF (
84
102
0f ,
85
103
0f ,
86
104
width.coerceAtMost(height).toFloat(),
87
105
width.coerceAtMost(height).toFloat()
88
106
)
107
+ }
89
108
90
- val scaledValues = scale()
109
+ override fun onDraw (canvas : Canvas ? ) {
110
+ super .onDraw(canvas)
91
111
92
- for (i in scaledValues.indices) {
93
- slicePaint.color = ContextCompat .getColor(context, sliceColors[i])
94
- canvas!! .drawArc(rectF!! , sliceStartPoint, scaledValues[i], true , slicePaint)
95
- pointsArray.add(Pair (sliceStartPoint, sliceStartPoint + scaledValues[i]))
96
- sliceStartPoint + = scaledValues[i]
97
- }
112
+ if (pieChart != null ) {
113
+ slices.forEach { slice ->
114
+ val arc = slice.arc!!
115
+ if (currentSweepAngle > arc.startAngle + arc.sweepAngle) {
116
+ slicePaint.color = ContextCompat .getColor(context, slice.color)
117
+ canvas?.drawArc(
118
+ rectF!! ,
119
+ pieChart?.sliceStartPoint!! + arc.startAngle,
120
+ arc.sweepAngle,
121
+ true ,
122
+ slicePaint
123
+ )
124
+ } else {
125
+ if (currentSweepAngle > arc.startAngle) {
126
+ slicePaint.color = ContextCompat .getColor(context, slice.color)
127
+ canvas?.drawArc(
128
+ rectF!! ,
129
+ pieChart?.sliceStartPoint!! + arc.startAngle,
130
+ currentSweepAngle - arc.startAngle,
131
+ true ,
132
+ slicePaint
133
+ )
134
+ }
135
+ }
136
+ }
98
137
99
- val centerX = (measuredWidth / 2 ).toFloat()
100
- val centerY = (measuredHeight / 2 ).toFloat()
101
- val radius = centerX.coerceAtMost(centerY)
138
+ val centerX = (measuredWidth / 2 ).toFloat()
139
+ val centerY = (measuredHeight / 2 ).toFloat()
140
+ val radius = centerX.coerceAtMost(centerY)
102
141
103
- canvas!! .drawCircle(rectF!! .centerX(), rectF!! .centerY(), radius - sliceWidth, centerPaint)
142
+ canvas!! .drawCircle(
143
+ rectF!! .centerX(),
144
+ rectF!! .centerY(),
145
+ radius - pieChart?.sliceWidth!! ,
146
+ centerPaint
147
+ )
148
+ }
104
149
}
105
150
106
- private fun getTotal (): Float = dataPoints.sum()
107
-
108
151
override fun onTouchEvent (event : MotionEvent ? ): Boolean {
109
152
return when (event?.action) {
110
153
MotionEvent .ACTION_DOWN -> {
@@ -120,7 +163,8 @@ class ClickablePieChart @JvmOverloads constructor(
120
163
)
121
164
)
122
165
123
- touchAngle - = sliceStartPoint
166
+ // FIXME: 16-Aug-20 Remove subtraction if unnecessary. On runtime sliceStartPoint is always 0f.
167
+ // touchAngle -= sliceStartPoint
124
168
touchAngle % = 360
125
169
126
170
if (touchAngle < 0 ) {
@@ -129,10 +173,10 @@ class ClickablePieChart @JvmOverloads constructor(
129
173
130
174
var total = 0.0f
131
175
var forEachStopper = false // what a idiot stuff
132
- dataPoints .forEachIndexed { index, data ->
133
- total + = data % 360f
176
+ slices .forEachIndexed { index, slice ->
177
+ total + = slice.dataPoint % 360f
134
178
if (touchAngle <= total && ! forEachStopper) {
135
- clickListener?.invoke(touchAngle.toString(), index.toFloat())
179
+ pieChart?. clickListener?.invoke(touchAngle.toString(), index.toFloat())
136
180
forEachStopper = true
137
181
showInfoPopup(index)
138
182
}
@@ -150,13 +194,14 @@ class ClickablePieChart @JvmOverloads constructor(
150
194
val width = LinearLayout .LayoutParams .WRAP_CONTENT
151
195
val height = LinearLayout .LayoutParams .WRAP_CONTENT
152
196
val popupWindow = PopupWindow (popupView, width, height, true )
153
- var center = pointsArray [index].toList() .average()
197
+ var center = slices [index].arc? .average()!!
154
198
val halfRadius = rectF!! .centerX()
155
199
156
- popupView.findViewById<TextView >(R .id.textViewPopupText).text = " ${center.toInt()} $popupText "
200
+ popupView.findViewById<TextView >(R .id.textViewPopupText).text =
201
+ " ${center.toInt()} $popupText "
157
202
ImageViewCompat .setImageTintList(
158
203
popupView.findViewById<ImageView >(R .id.imageViewPopupCircleIndicator),
159
- ColorStateList .valueOf(ContextCompat .getColor(context, sliceColors [index]))
204
+ ColorStateList .valueOf(ContextCompat .getColor(context, slices [index].color ))
160
205
)
161
206
162
207
val calculatedX =
@@ -167,7 +212,7 @@ class ClickablePieChart @JvmOverloads constructor(
167
212
val currentViewLocation = IntArray (2 )
168
213
this .getLocationOnScreen(currentViewLocation)
169
214
170
- val halfOfSliceWidth = (sliceWidth / 2 ).toInt()
215
+ val halfOfSliceWidth = (pieChart?. sliceWidth?.p2d(context) !! / 2 ).toInt()
171
216
val popupWindowX =
172
217
(currentViewLocation[0 ] + halfRadius.toInt()) + calculatedX -
173
218
(if (calculatedX < 0 ) - halfOfSliceWidth else halfOfSliceWidth)
@@ -189,38 +234,18 @@ class ClickablePieChart @JvmOverloads constructor(
189
234
popupWindow.height
190
235
)
191
236
}
192
-
193
- val currentData = dataPoints[index]
194
-
195
- }
196
-
197
- fun setSliceWidth (width : Float ) {
198
- sliceWidth = width.p2d(context)
199
237
}
200
238
201
- fun setListener (listener : (String , Float ) -> (Unit )) {
202
- clickListener = listener
203
- }
204
-
205
- fun setStartPoint (point : Float ) {
206
- sliceStartPoint = point
207
- }
208
-
209
- fun setDataPoints (data : FloatArray ) {
210
- dataPoints = data
211
- invalidateAndRequestLayout()
239
+ fun setPieChart (pieChart : PieChart ) {
240
+ this .pieChart = pieChart
241
+ init ()
212
242
}
213
243
214
244
fun setCenterColor (colorId : Int ) {
215
245
centerPaint.color = ContextCompat .getColor(context, colorId)
216
246
invalidateAndRequestLayout()
217
247
}
218
248
219
- fun setSliceColor (colors : IntArray ) {
220
- sliceColors = colors
221
- invalidateAndRequestLayout()
222
- }
223
-
224
249
private fun invalidateAndRequestLayout () {
225
250
invalidate()
226
251
requestLayout()
0 commit comments