diff --git a/MPChartLib/src/main/res/values/attrs.xml b/MPChartLib/src/main/res/values/attrs.xml
new file mode 100644
index 0000000000..b41e435222
--- /dev/null
+++ b/MPChartLib/src/main/res/values/attrs.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/chartLib/src/main/kotlin/info/appdev/charting/charts/PieChart2.kt b/chartLib/src/main/kotlin/info/appdev/charting/charts/PieChart2.kt
new file mode 100644
index 0000000000..90565ba148
--- /dev/null
+++ b/chartLib/src/main/kotlin/info/appdev/charting/charts/PieChart2.kt
@@ -0,0 +1,732 @@
+package info.appdev.charting.charts
+
+import android.content.Context
+import android.content.res.TypedArray
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.RectF
+import android.graphics.Typeface
+import android.text.TextUtils
+import android.util.AttributeSet
+import info.appdev.charting.components.XAxis
+import info.appdev.charting.data.PieData
+import info.appdev.charting.data.PieEntry
+import info.appdev.charting.highlight.Highlight
+import info.appdev.charting.highlight.PieHighlighter
+import info.appdev.charting.interfaces.datasets.IPieDataSet
+import info.appdev.charting.renderer.PieChartRenderer
+import info.appdev.charting.renderer.PieChartRendererFixCover
+import info.appdev.charting.utils.PointF
+import info.appdev.charting.utils.Utils
+import info.appdev.charting.utils.getNormalizedAngle
+import java.util.Locale
+import kotlin.math.abs
+import kotlin.math.cos
+import kotlin.math.min
+import kotlin.math.sin
+
+/**
+ * View that represents a pie chart. Draws cake like slices.
+ */
+class PieChart2 : PieRadarChartBase {
+ private var mode: String? = null
+
+ /**
+ * rect object that represents the bounds of the piechart, needed for
+ * drawing the circle
+ */
+ private val mCircleBox: RectF = RectF()
+
+ /**
+ * Returns true if drawing the entry labels is enabled, false if not.
+ *
+ * @return
+ */
+ /**
+ * flag indicating if entry labels should be drawn or not
+ */
+ var isDrawEntryLabelsEnabled: Boolean = true
+ private set
+
+ /**
+ * returns an integer array of all the different angles the chart slices
+ * have the angles in the returned array determine how much space (of 360°)
+ * each slice takes
+ *
+ * @return
+ */
+ /**
+ * array that holds the width of each pie-slice in degrees
+ */
+ var drawAngles: FloatArray = FloatArray(1)
+ private set
+
+ /**
+ * returns the absolute angles of the different chart slices (where the
+ * slices end)
+ *
+ * @return
+ */
+ /**
+ * array that holds the absolute angle in degrees of each slice
+ */
+ var absoluteAngles: FloatArray = FloatArray(1)
+ private set
+
+ /**
+ * returns true if the hole in the center of the pie-chart is set to be
+ * visible, false if not
+ *
+ * @return
+ */
+ /**
+ * set this to true to draw the pie center empty
+ *
+ * @param enabled
+ */
+ /**
+ * if true, the white hole inside the chart will be drawn
+ */
+ var isDrawHoleEnabled: Boolean = true
+
+ /**
+ * Returns true if the inner tips of the slices are visible behind the hole,
+ * false if not.
+ *
+ * @return true if slices are visible behind the hole.
+ */
+ /**
+ * if true, the hole will see-through to the inner tips of the slices
+ */
+ var isDrawSlicesUnderHoleEnabled: Boolean = false
+ private set
+
+ /**
+ * Returns true if using percentage values is enabled for the chart.
+ *
+ * @return
+ */
+ /**
+ * if true, the values inside the piechart are drawn as percent values
+ */
+ var isUsePercentValuesEnabled: Boolean = false
+ private set
+
+ /**
+ * Returns true if the chart is set to draw each end of a pie-slice
+ * "rounded".
+ *
+ * @return
+ */
+ /**
+ * if true, the slices of the piechart are rounded
+ */
+ var isDrawRoundedSlicesEnabled: Boolean = false
+ private set
+
+ /**
+ * variable for the text that is drawn in the center of the pie-chart
+ */
+ private var mCenterText: CharSequence? = ""
+
+ private val mCenterTextOffset: PointF = PointF.getInstance(0F, 0F)
+
+ /**
+ * Returns the size of the hole radius in percent of the total radius.
+ *
+ * @return
+ */
+ /**
+ * sets the radius of the hole in the center of the piechart in percent of
+ * the maximum radius (max = the radius of the whole chart), default 50%
+ *
+ * @param percent
+ */
+ /**
+ * indicates the size of the hole in the center of the piechart, default:
+ * radius / 2
+ */
+ var holeRadius: Float = 50f
+
+ /**
+ * sets the radius of the transparent circle that is drawn next to the hole
+ * in the piechart in percent of the maximum radius (max = the radius of the
+ * whole chart), default 55% -> means 5% larger than the center-hole by
+ * default
+ *
+ * @param percent
+ */
+ /**
+ * the radius of the transparent circle next to the chart-hole in the center
+ */
+ var transparentCircleRadius: Float = 55f
+
+ /**
+ * returns true if drawing the center text is enabled
+ *
+ * @return
+ */
+ /**
+ * if enabled, centertext is drawn
+ */
+ var isDrawCenterTextEnabled: Boolean = true
+ private set
+
+ /**
+ * the rectangular radius of the bounding box for the center text, as a percentage of the pie
+ * hole
+ * default 1.f (100%)
+ */
+ /**
+ * the rectangular radius of the bounding box for the center text, as a percentage of the pie
+ * hole
+ * default 1.f (100%)
+ */
+ var centerTextRadiusPercent: Float = 100f
+
+ protected var mMaxAngle: Float = 360f
+
+ /**
+ * Minimum angle to draw slices, this only works if there is enough room for all slices to have
+ * the minimum angle, default 0f.
+ */
+ private var mMinAngleForSlices = 0f
+
+ constructor(context: Context?) : super(context)
+
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+
+ constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) {
+ getAttrs(attrs)
+ }
+
+ private fun getAttrs(attrs: AttributeSet?) {
+ if (attrs != null) {
+ val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.PieChart)
+ mode = a.getString(R.styleable.PieChart_mp_chart_out_value_place_mode)
+ a.recycle()
+ }
+ (mRenderer as PieChartRendererFixCover).mode = mode
+ }
+
+
+ override fun init() {
+ super.init()
+
+ mRenderer = PieChartRendererFixCover(this, mAnimator, viewPortHandler)
+
+ highlighter = PieHighlighter(this)
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ if (mData == null) return
+
+ mRenderer.drawData(canvas)
+
+ if (valuesToHighlight())
+ mRenderer?.drawHighlighted(canvas, indicesToHighlight)
+
+ mRenderer?.drawExtras(canvas)
+
+ mRenderer?.drawValues(canvas)
+
+ legendRenderer?.renderLegend(canvas)
+
+ drawDescription(canvas)
+
+ drawMarkers(canvas)
+ }
+
+ override fun calculateOffsets() {
+ super.calculateOffsets()
+
+ // prevent null pointer when no data set
+ if (mData == null)
+ return
+
+ val radius = diameter / 2f
+
+ val c: PointF = getCenterOffsets()
+
+ val shift: Float = mData.getDataSet().getSelectionShift()
+
+ // create the circle box that will contain the pie-chart (the bounds of
+ // the pie-chart)
+ mCircleBox.set(
+ c.x - radius + shift,
+ c.y - radius + shift,
+ c.x + radius - shift,
+ c.y + radius - shift
+ )
+
+ PointF.recycleInstance(c)
+ }
+
+ override fun calcMinMax() {
+ calcAngles()
+ }
+
+ override fun getMarkerPosition(high: Highlight): FloatArray {
+ val center: PointF = this.centerCircleBox
+ var r = this.radius
+
+ var off = r / 10f * 3.6f
+
+ if (this.isDrawHoleEnabled) {
+ off = (r - (r / 100f * this.holeRadius)) / 2f
+ }
+
+ r -= off // offset to keep things inside the chart
+
+ val entryIndex = high.x.toInt()
+
+ // offset needed to center the drawn text in the slice
+ val offset = this.drawAngles[entryIndex] / 2
+
+ // calculate the text position
+ val x = (r
+ * cos(
+ Math.toRadians(
+ ((rotationAngle + this.absoluteAngles[entryIndex] - offset)
+ * mAnimator.phaseY).toDouble()
+ )
+ ) + center.x).toFloat()
+ val y = (r
+ * sin(
+ Math.toRadians(
+ ((rotationAngle + this.absoluteAngles[entryIndex] - offset)
+ * mAnimator.phaseY).toDouble()
+ )
+ ) + center.y).toFloat()
+
+ PointF.recycleInstance(center)
+ return floatArrayOf(x, y)
+ }
+
+ /**
+ * calculates the needed angles for the chart slices
+ */
+ private fun calcAngles() {
+ val entryCount: Int = mData!!.entryCount
+
+ if (drawAngles.size != entryCount) {
+ this.drawAngles = FloatArray(entryCount)
+ } else {
+ for (i in 0.. a) return i
+ }
+
+ return -1 // return -1 if no index found
+ }
+
+ /**
+ * Returns the index of the DataSet this x-index belongs to.
+ *
+ * @param xIndex
+ * @return
+ */
+ fun getDataSetIndexForIndex(xIndex: Int): Int {
+ val dataSets: MutableList = mData.getDataSets()
+
+ for (i in dataSets.indices) {
+ if (dataSets.get(i).getEntryForXValue(xIndex, Float.NaN) != null) return i
+ }
+
+ return -1
+ }
+
+ /**
+ * Sets the color for the hole that is drawn in the center of the PieChart
+ * (if enabled).
+ *
+ * @param color
+ */
+ fun setHoleColor(color: Int) {
+ (mRenderer as PieChartRenderer).getPaintHole().setColor(color)
+ }
+
+ /**
+ * Enable or disable the visibility of the inner tips of the slices behind the hole
+ */
+ fun setDrawSlicesUnderHole(enable: Boolean) {
+ this.isDrawSlicesUnderHoleEnabled = enable
+ }
+
+ var centerText: CharSequence?
+ /**
+ * returns the text that is drawn in the center of the pie-chart
+ *
+ * @return
+ */
+ get() = mCenterText
+ /**
+ * Sets the text String that is displayed in the center of the PieChart.
+ *
+ * @param text
+ */
+ set(text) {
+ if (text == null) mCenterText = ""
+ else mCenterText = text
+ }
+
+ /**
+ * set this to true to draw the text that is displayed in the center of the
+ * pie chart
+ *
+ * @param enabled
+ */
+ fun setDrawCenterText(enabled: Boolean) {
+ this.isDrawCenterTextEnabled = enabled
+ }
+
+ val requiredLegendOffset: Float
+ get() = mLegendRenderer.getLabelPaint().getTextSize() * 2f
+
+ val requiredBaseOffset: Float
+ get() = 0f
+
+ val radius: Float
+ get() {
+ if (mCircleBox == null) return 0f
+ else return min(mCircleBox.width() / 2f, mCircleBox.height() / 2f)
+ }
+
+ val circleBox: RectF
+ /**
+ * returns the circlebox, the boundingbox of the pie-chart slices
+ *
+ * @return
+ */
+ get() = mCircleBox
+
+ val centerCircleBox: PointF
+ /**
+ * returns the center of the circlebox
+ *
+ * @return
+ */
+ get() = PointF.getInstance(mCircleBox.centerX(), mCircleBox.centerY())
+
+ /**
+ * sets the typeface for the center-text paint
+ *
+ * @param t
+ */
+ fun setCenterTextTypeface(t: Typeface?) {
+ (mRenderer as PieChartRenderer).getPaintCenterText().setTypeface(t)
+ }
+
+ /**
+ * Sets the size of the center text of the PieChart in dp.
+ *
+ * @param sizeDp
+ */
+ fun setCenterTextSize(sizeDp: Float) {
+ (mRenderer as PieChartRenderer).getPaintCenterText().setTextSize(
+ convertDpToPixel(sizeDp)
+ )
+ }
+
+ /**
+ * Sets the size of the center text of the PieChart in pixels.
+ *
+ * @param sizePixels
+ */
+ fun setCenterTextSizePixels(sizePixels: Float) {
+ (mRenderer as PieChartRenderer).getPaintCenterText().setTextSize(sizePixels)
+ }
+
+ /**
+ * Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0
+ *
+ * @param x
+ * @param y
+ */
+ fun setCenterTextOffset(x: Float, y: Float) {
+ mCenterTextOffset.x = convertDpToPixel(x)
+ mCenterTextOffset.y = convertDpToPixel(y)
+ }
+
+ val centerTextOffset: PointF
+ /**
+ * Returns the offset on the x- and y-axis the center text has in dp.
+ *
+ * @return
+ */
+ get() = PointF.getInstance(mCenterTextOffset.x, mCenterTextOffset.y)
+
+ /**
+ * Sets the color of the center text of the PieChart.
+ *
+ * @param color
+ */
+ fun setCenterTextColor(color: Int) {
+ (mRenderer as PieChartRenderer).getPaintCenterText().setColor(color)
+ }
+
+ /**
+ * Sets the color the transparent-circle should have.
+ *
+ * @param color
+ */
+ fun setTransparentCircleColor(color: Int) {
+ val p: Paint = (mRenderer as PieChartRenderer).getPaintTransparentCircle()
+ val alpha = p.alpha
+ p.color = color
+ p.alpha = alpha
+ }
+
+ /**
+ * Sets the amount of transparency the transparent circle should have 0 = fully transparent,
+ * 255 = fully opaque.
+ * Default value is 100.
+ *
+ * @param alpha 0-255
+ */
+ fun setTransparentCircleAlpha(alpha: Int) {
+ (mRenderer as PieChartRenderer).getPaintTransparentCircle().setAlpha(alpha)
+ }
+
+ /**
+ * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class).
+ * Deprecated -> use setDrawEntryLabels(...) instead.
+ *
+ * @param enabled
+ */
+ @Deprecated("")
+ fun setDrawSliceText(enabled: Boolean) {
+ this.isDrawEntryLabelsEnabled = enabled
+ }
+
+ /**
+ * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class).
+ *
+ * @param enabled
+ */
+ fun setDrawEntryLabels(enabled: Boolean) {
+ this.isDrawEntryLabelsEnabled = enabled
+ }
+
+ /**
+ * Sets the color the entry labels are drawn with.
+ *
+ * @param color
+ */
+ fun setEntryLabelColor(color: Int) {
+ (mRenderer as PieChartRenderer).getPaintEntryLabels().setColor(color)
+ }
+
+ /**
+ * Sets a custom Typeface for the drawing of the entry labels.
+ *
+ * @param tf
+ */
+ fun setEntryLabelTypeface(tf: Typeface?) {
+ (mRenderer as PieChartRenderer).getPaintEntryLabels().setTypeface(tf)
+ }
+
+ /**
+ * Sets the size of the entry labels in dp. Default: 13dp
+ *
+ * @param size
+ */
+ fun setEntryLabelTextSize(size: Float) {
+ (mRenderer as PieChartRenderer).getPaintEntryLabels().setTextSize(convertDpToPixel(size))
+ }
+
+ /**
+ * Sets whether to draw slices in a curved fashion, only works if drawing the hole is enabled
+ * and if the slices are not drawn under the hole.
+ *
+ * @param enabled draw curved ends of slices
+ */
+ fun setDrawRoundedSlices(enabled: Boolean) {
+ this.isDrawRoundedSlicesEnabled = enabled
+ }
+
+ /**
+ * If this is enabled, values inside the PieChart are drawn in percent and
+ * not with their original value. Values provided for the IValueFormatter to
+ * format are then provided in percent.
+ *
+ * @param enabled
+ */
+ fun setUsePercentValues(enabled: Boolean) {
+ this.isUsePercentValuesEnabled = enabled
+ }
+
+ var maxAngle: Float
+ get() = mMaxAngle
+ /**
+ * Sets the max angle that is used for calculating the pie-circle. 360f means
+ * it's a full PieChart, 180f results in a half-pie-chart. Default: 360f
+ *
+ * @param maxangle min 90, max 360
+ */
+ set(maxangle) {
+ var maxangle = maxangle
+ if (maxangle > 360) maxangle = 360f
+
+ if (maxangle < 90) maxangle = 90f
+
+ this.mMaxAngle = maxangle
+ }
+
+ var minAngleForSlices: Float
+ /**
+ * The minimum angle slices on the chart are rendered with, default is 0f.
+ *
+ * @return minimum angle for slices
+ */
+ get() = mMinAngleForSlices
+ /**
+ * Set the angle to set minimum size for slices, you must call [.notifyDataSetChanged]
+ * and [.invalidate] when changing this, only works if there is enough room for all
+ * slices to have the minimum angle.
+ *
+ * @param minAngle minimum 0, maximum is half of [.setMaxAngle]
+ */
+ set(minAngle) {
+ var minAngle = minAngle
+ if (minAngle > (mMaxAngle / 2f)) minAngle = mMaxAngle / 2f
+ else if (minAngle < 0) minAngle = 0f
+
+ this.mMinAngleForSlices = minAngle
+ }
+
+ override fun onDetachedFromWindow() {
+ // releases the bitmap in the renderer to avoid oom error
+ if (mRenderer != null && mRenderer is PieChartRenderer) {
+ (mRenderer as PieChartRenderer).releaseBitmap()
+ }
+ super.onDetachedFromWindow()
+ }
+
+ val accessibilityDescription: String
+ get() {
+ val pieData: PieData? = getData()
+
+ var entryCount = 0
+ if (pieData != null) entryCount = pieData.getEntryCount()
+
+ val builder = StringBuilder()
+
+ builder.append(String.format(Locale.getDefault(), "The pie chart has %d entries.", entryCount))
+
+ for (i in 0..= 0) {
+ rightToLeftCount++
+ } else if (rotationAngle % 360 in 90.0..270.0 && rotationAngle != 270f && angle * phaseY % 360.0 > 180.0 && angle * phaseY % 360.0 < 360.0) {
+ leftToLeftCount++
+ }
+ } else {
+ rightCount++
+ if (rotationAngle != 270f && angle * phaseY % 360.0 > 180.0 && angle * phaseY % 360.0 < 360.0) {
+ leftToRightCount++
+ } else if (rotationAngle % 360 in 90.0..270.0 && rotationAngle != 270f && angle * phaseY % 360.0 <= 180.0 && angle * phaseY % 360.0 >= 0) {
+ rightToRightCount++
+ }
+ }
+ }
+ xIndex++
+ }
+ xIndex = 0
+ val measuredHeight = chart.measuredHeight
+ val topAndBottomSpace = measuredHeight - radius * 2
+ val rightSpace = radius * 2 / (rightCount - 1)
+ val leftSpace = radius * 2 / (leftCount - 1)
+ var tempRightIndex = 0
+ var tempLeftIndex = 0
+ var tempLeftToRightIndex = 0
+ var tempRightToRightIndex = 0
+ var tempRightToLeftIndex = 0
+ var tempLeftToLeftIndex = 0
+ for (j in 0 until entryCount) {
+ dataSet.getEntryForIndex(j)?.let { entry ->
+ angle = if (xIndex == 0) 0f else absoluteAngles[xIndex - 1] * phaseX
+ val sliceAngle = drawAngles[xIndex]
+ val sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius)
+
+ // offset needed to center the drawn text in the slice
+ val angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2f) / 2f
+ angle += angleOffset
+ val transformedAngle = rotationAngle + angle * phaseY
+ val value: Float = if (chart.isUsePercentValuesEnabled)
+ entry.y / yValueSum * 100f
+ else
+ entry.y
+ val sliceXBase = cos((transformedAngle * Utils.FDEG2RAD).toDouble()).toFloat()
+ val sliceYBase = sin((transformedAngle * Utils.FDEG2RAD).toDouble()).toFloat()
+ val drawXOutside = drawEntryLabels &&
+ xValuePosition == ValuePosition.OUTSIDE_SLICE
+ val drawYOutside = drawValues &&
+ yValuePosition == ValuePosition.OUTSIDE_SLICE
+ val drawXInside = drawEntryLabels &&
+ xValuePosition == ValuePosition.INSIDE_SLICE
+ val drawYInside = drawValues &&
+ yValuePosition == ValuePosition.INSIDE_SLICE
+ if (drawXOutside || drawYOutside) {
+ val valueLineLength1 = dataSet.valueLinePart1Length
+ val valueLineLength2 = dataSet.valueLinePart2Length
+ val valueLinePart1OffsetPercentage = dataSet.valueLinePart1OffsetPercentage / 100f
+ var pt2x: Float
+ var pt2y: Float
+ var labelPtx: Float
+ var labelPty: Float
+ val line1Radius: Float = if (chart.isDrawHoleEnabled) (radius - radius * holeRadiusPercent
+ * valueLinePart1OffsetPercentage) + radius * holeRadiusPercent else radius * valueLinePart1OffsetPercentage
+ if (dataSet.isValueLineVariableLength) labelRadius * valueLineLength2 *
+ abs(sin((transformedAngle * Utils.FDEG2RAD).toDouble())).toFloat()
+ else
+ labelRadius * valueLineLength2
+ val pt0x = line1Radius * sliceXBase + center.x
+ val pt0y = line1Radius * sliceYBase + center.y
+ val pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x
+ val pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y
+ if (transformedAngle % 360.0 in 90.0..270.0) {
+ pt2x = center.x - radius - 5
+ if (rotationAngle != 270f && angle * phaseY % 360.0 <= 180.0 && angle * phaseY % 360.0 >= 0) {
+ pt2y = measuredHeight - topAndBottomSpace / 2 - leftSpace * (tempRightToLeftIndex + leftToLeftCount)
+ tempRightToLeftIndex++
+ tempLeftIndex++
+ } else if (rotationAngle % 360 in 90.0..270.0 && rotationAngle != 270f && angle * phaseY % 360.0 > 180.0 && angle * phaseY % 360.0 < 360.0) {
+ pt2y = measuredHeight - topAndBottomSpace / 2 - leftSpace * tempLeftToLeftIndex
+ tempLeftToLeftIndex++
+ } else {
+ pt2y = measuredHeight - topAndBottomSpace / 2 - leftSpace * (tempLeftIndex + leftToLeftCount)
+ tempLeftIndex++
+ }
+ paintValues.textAlign = Paint.Align.RIGHT
+ if (drawXOutside) paintEntryLabels.textAlign = Paint.Align.RIGHT
+ labelPtx = pt2x - offset
+ labelPty = pt2y
+ } else {
+ pt2x = center.x + radius + 5
+ if (rotationAngle != 270f && angle * phaseY % 360.0 > 180.0 && angle * phaseY % 360.0 < 360.0) {
+ pt2y = topAndBottomSpace / 2 + rightSpace * (tempLeftToRightIndex + rightToRightCount)
+ tempLeftToRightIndex++
+ tempRightIndex++
+ } else if (rotationAngle % 360 in 90.0..270.0 && rotationAngle != 270f && angle * phaseY % 360.0 <= 180.0 && angle * phaseY % 360.0 >= 0) {
+ pt2y = topAndBottomSpace / 2 + rightSpace * tempRightToRightIndex
+ tempRightIndex++
+ tempRightToRightIndex++
+ } else {
+ pt2y = topAndBottomSpace / 2 + rightSpace * (tempRightIndex + leftToRightCount + rightToRightCount)
+ tempRightIndex++
+ }
+ paintValues.textAlign = Paint.Align.LEFT
+ if (drawXOutside) paintEntryLabels.textAlign = Paint.Align.LEFT
+ labelPtx = pt2x + offset
+ labelPty = pt2y
+ }
+ if (dataSet.valueLineColor != ColorTemplate.COLOR_NONE) {
+ canvas.drawLine(pt0x, pt0y, pt1x, pt1y, valueLinePaint)
+ canvas.drawLine(pt1x, pt1y, pt2x, pt2y, valueLinePaint)
+ }
+
+ // draw everything, depending on settings
+ if (drawXOutside && drawYOutside) {
+ drawValue(
+ canvas,
+ formatter,
+ value,
+ entry,
+ 0,
+ labelPtx,
+ labelPty,
+ dataSet.getValueTextColor(j)
+ )
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(canvas, entry.label!!, labelPtx, labelPty + lineHeight)
+ }
+ } else if (drawXOutside) {
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(canvas, entry.label!!, labelPtx, labelPty + lineHeight / 2f)
+ }
+ } else if (drawYOutside) {
+ drawValue(
+ canvas, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2f, dataSet
+ .getValueTextColor(j)
+ )
+ }
+ }
+ if (drawXInside || drawYInside) {
+ // calculate the text position
+ val x = labelRadius * sliceXBase + center.x
+ val y = labelRadius * sliceYBase + center.y
+ paintValues.textAlign = Paint.Align.CENTER
+
+ // draw everything, depending on settings
+ if (drawXInside && drawYInside) {
+ drawValue(canvas, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j))
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(canvas, entry.label!!, x, y + lineHeight)
+ }
+ } else if (drawXInside) {
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(canvas, entry.label!!, x, y + lineHeight / 2f)
+ }
+ } else if (drawYInside) {
+ drawValue(canvas, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j))
+ }
+ }
+ if (entry.icon != null && dataSet.isDrawIcons) {
+ val icon = entry.icon
+ val x = (labelRadius + iconsOffset.y) * sliceXBase + center.x
+ var y = (labelRadius + iconsOffset.y) * sliceYBase + center.y
+ y += iconsOffset.x
+ icon?.let { ic ->
+ canvas.drawImage(
+ ic, x.toInt(), y.toInt(),
+ )
+ }
+ }
+ }
+ xIndex++
+ }
+ PointF.recycleInstance(iconsOffset)
+ }
+ }
+ PointF.recycleInstance(center)
+ canvas.restore()
+ }
+
+ private fun drawValuesTopAlign(c: Canvas) {
+ val rect = Rect()
+ paintEntryLabels.getTextBounds(text, 0, text.length, rect)
+ val textHeight = rect.height()
+ val center = chart.centerCircleBox
+
+ // get whole the radius
+ val radius = chart.radius
+ val rotationAngle = chart.rotationAngle
+ val drawAngles = chart.drawAngles
+ val absoluteAngles = chart.absoluteAngles
+ val phaseX = animator.phaseX
+ val phaseY = animator.phaseY
+ val holeRadiusPercent = chart.holeRadius / 100f
+ var labelRadiusOffset = radius / 10f * 3.6f
+ if (chart.isDrawHoleEnabled) {
+ labelRadiusOffset = radius - radius * holeRadiusPercent / 2f
+ }
+ val labelRadius = radius - labelRadiusOffset
+ val data = chart.getData()
+ val dataSets = data?.dataSets
+ val yValueSum = data?.yValueSum ?: 0F
+ val drawEntryLabels = chart.isDrawEntryLabelsEnabled
+ var angle: Float
+ var xIndex = 0
+ c.save()
+ val offset = 5f.convertDpToPixel()
+ dataSets?.let {
+ for (i in it.indices) {
+ val dataSet = dataSets[i]
+ val drawValues = dataSet.isDrawValues
+ if (!drawValues && !drawEntryLabels) continue
+ val xValuePosition = dataSet.xValuePosition
+ val yValuePosition = dataSet.yValuePosition
+
+ // apply the text-styling defined by the DataSet
+ applyValueTextStyle(dataSet)
+ val lineHeight = (paintValues.calcTextHeight("Q")
+ + 4f.convertDpToPixel())
+ val formatter = dataSet.valueFormatter
+ val entryCount = dataSet.entryCount
+ valueLinePaint.color = dataSet.valueLineColor
+ valueLinePaint.strokeWidth = dataSet.valueLineWidth.convertDpToPixel()
+ val sliceSpace = getSliceSpace(dataSet)
+ val iconsOffset = PointF.getInstance(dataSet.iconsOffset)
+ iconsOffset.x = iconsOffset.x.convertDpToPixel()
+ iconsOffset.y = iconsOffset.y.convertDpToPixel()
+ var lastPositionOfLeft = 0f
+ var lastPositionOfRight = 0f
+ for (j in 0 until entryCount) {
+ dataSet.getEntryForIndex(j)?.let { entry ->
+ angle = if (xIndex == 0) 0f else absoluteAngles[xIndex - 1] * phaseX
+ val sliceAngle = drawAngles[xIndex]
+ val sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius)
+
+ // offset needed to center the drawn text in the slice
+ val angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2f) / 2f
+ angle += angleOffset
+ val transformedAngle = rotationAngle + angle * phaseY
+ val value: Float = if (chart.isUsePercentValuesEnabled)
+ entry.y / yValueSum * 100f
+ else
+ entry.y
+ val sliceXBase = cos((transformedAngle * Utils.FDEG2RAD).toDouble()).toFloat()
+ val sliceYBase = sin((transformedAngle * Utils.FDEG2RAD).toDouble()).toFloat()
+ val drawXOutside = drawEntryLabels &&
+ xValuePosition == ValuePosition.OUTSIDE_SLICE
+ val drawYOutside = drawValues &&
+ yValuePosition == ValuePosition.OUTSIDE_SLICE
+ val drawXInside = drawEntryLabels &&
+ xValuePosition == ValuePosition.INSIDE_SLICE
+ val drawYInside = drawValues &&
+ yValuePosition == ValuePosition.INSIDE_SLICE
+ if (drawXOutside || drawYOutside) {
+ val valueLineLength1 = dataSet.valueLinePart1Length
+ val valueLineLength2 = dataSet.valueLinePart2Length
+ val valueLinePart1OffsetPercentage = dataSet.valueLinePart1OffsetPercentage / 100f
+ var pt2x: Float
+ var pt2y: Float
+ var labelPtx: Float
+ var labelPty: Float
+ val line1Radius: Float = if (chart.isDrawHoleEnabled) (radius - radius * holeRadiusPercent
+ * valueLinePart1OffsetPercentage) + radius * holeRadiusPercent else radius * valueLinePart1OffsetPercentage
+ if (dataSet.isValueLineVariableLength) labelRadius * valueLineLength2 *
+ abs(sin((transformedAngle * Utils.FDEG2RAD).toDouble())).toFloat()
+ else
+ labelRadius * valueLineLength2
+ val pt0x = line1Radius * sliceXBase + center.x
+ val pt0y = line1Radius * sliceYBase + center.y
+ val pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x
+ val pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y
+ if (transformedAngle % 360.0 in 90.0..270.0) {
+ break
+ } else {
+ pt2x = center.x + radius + 5
+ pt2y = if (lastPositionOfRight == 0f) {
+ pt1y
+ } else {
+ if (pt1y - lastPositionOfRight < textHeight) {
+ pt1y + (textHeight - (pt1y - lastPositionOfRight))
+ } else {
+ pt1y
+ }
+ }
+ lastPositionOfRight = pt2y
+ paintValues.textAlign = Paint.Align.LEFT
+ if (drawXOutside) paintEntryLabels.textAlign = Paint.Align.LEFT
+ labelPtx = pt2x + offset
+ labelPty = pt2y
+ }
+ if (dataSet.valueLineColor != ColorTemplate.COLOR_NONE) {
+ c.drawLine(pt0x, pt0y, pt1x, pt1y, valueLinePaint)
+ c.drawLine(pt1x, pt1y, pt2x, pt2y, valueLinePaint)
+ }
+
+ // draw everything, depending on settings
+ if (drawXOutside && drawYOutside) {
+ drawValue(
+ c,
+ formatter,
+ value,
+ entry,
+ 0,
+ labelPtx,
+ labelPty,
+ dataSet.getValueTextColor(j)
+ )
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, labelPtx, labelPty + lineHeight)
+ }
+ } else if (drawXOutside) {
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, labelPtx, labelPty + lineHeight / 2f)
+ }
+ } else if (drawYOutside) {
+ drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2f, dataSet.getValueTextColor(j))
+ }
+ }
+ if (drawXInside || drawYInside) {
+ // calculate the text position
+ val x = labelRadius * sliceXBase + center.x
+ val y = labelRadius * sliceYBase + center.y
+ paintValues.textAlign = Paint.Align.CENTER
+
+ // draw everything, depending on settings
+ if (drawXInside && drawYInside) {
+ drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j))
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, x, y + lineHeight)
+ }
+ } else if (drawXInside) {
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, x, y + lineHeight / 2f)
+ }
+ } else if (drawYInside) {
+ drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j))
+ }
+ }
+ if (entry.icon != null && dataSet.isDrawIcons) {
+ val icon = entry.icon
+ val x = (labelRadius + iconsOffset.y) * sliceXBase + center.x
+ var y = (labelRadius + iconsOffset.y) * sliceYBase + center.y
+ y += iconsOffset.x
+ icon?.let { ic ->
+ c.drawImage(
+ ic, x.toInt(), y.toInt()
+ )
+ }
+ }
+ }
+ xIndex++
+ }
+
+ //画左边
+ xIndex = entryCount - 1
+ for (j in entryCount - 1 downTo 0) {
+ dataSet.getEntryForIndex(j)?.let { entry ->
+ angle = if (xIndex == 0) 0f else absoluteAngles[xIndex - 1] * phaseX
+ val sliceAngle = drawAngles[xIndex]
+ val sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius)
+
+ // offset needed to center the drawn text in the slice
+ val angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2f) / 2f
+ angle += angleOffset
+ val transformedAngle = rotationAngle + angle * phaseY
+ val value: Float = if (chart.isUsePercentValuesEnabled)
+ entry.y / yValueSum * 100f
+ else
+ entry.y
+ val sliceXBase = cos((transformedAngle * Utils.FDEG2RAD).toDouble()).toFloat()
+ val sliceYBase = sin((transformedAngle * Utils.FDEG2RAD).toDouble()).toFloat()
+ val drawXOutside = drawEntryLabels &&
+ xValuePosition == ValuePosition.OUTSIDE_SLICE
+ val drawYOutside = drawValues &&
+ yValuePosition == ValuePosition.OUTSIDE_SLICE
+ val drawXInside = drawEntryLabels &&
+ xValuePosition == ValuePosition.INSIDE_SLICE
+ val drawYInside = drawValues &&
+ yValuePosition == ValuePosition.INSIDE_SLICE
+ if (drawXOutside || drawYOutside) {
+ val valueLineLength1 = dataSet.valueLinePart1Length
+ val valueLineLength2 = dataSet.valueLinePart2Length
+ val valueLinePart1OffsetPercentage = dataSet.valueLinePart1OffsetPercentage / 100f
+ var pt2x: Float
+ var pt2y: Float
+ var labelPtx: Float
+ var labelPty: Float
+ val line1Radius: Float = if (chart.isDrawHoleEnabled) (radius - radius * holeRadiusPercent
+ * valueLinePart1OffsetPercentage) + radius * holeRadiusPercent else radius * valueLinePart1OffsetPercentage
+ if (dataSet.isValueLineVariableLength) labelRadius * valueLineLength2 *
+ abs(sin((transformedAngle * Utils.FDEG2RAD).toDouble())).toFloat()
+ else
+ labelRadius * valueLineLength2
+ val pt0x = line1Radius * sliceXBase + center.x
+ val pt0y = line1Radius * sliceYBase + center.y
+ val pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x
+ val pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y
+ if (transformedAngle % 360.0 in 90.0..270.0) {
+ pt2x = center.x - radius - 5
+ pt2y = if (lastPositionOfLeft == 0f) {
+ pt1y
+ } else {
+ if (pt1y - lastPositionOfLeft < textHeight) {
+ pt1y + (textHeight - (pt1y - lastPositionOfLeft))
+ } else {
+ pt1y
+ }
+ }
+ lastPositionOfLeft = pt2y
+ paintValues.textAlign = Paint.Align.RIGHT
+ if (drawXOutside) paintEntryLabels.textAlign = Paint.Align.RIGHT
+ labelPtx = pt2x - offset
+ labelPty = pt2y
+ } else {
+ continue
+ }
+ if (dataSet.valueLineColor != ColorTemplate.COLOR_NONE) {
+ c.drawLine(pt0x, pt0y, pt1x, pt1y, valueLinePaint)
+ c.drawLine(pt1x, pt1y, pt2x, pt2y, valueLinePaint)
+ }
+
+ // draw everything, depending on settings
+ if (drawXOutside && drawYOutside) {
+ drawValue(
+ c,
+ formatter,
+ value,
+ entry,
+ 0,
+ labelPtx,
+ labelPty,
+ dataSet.getValueTextColor(j)
+ )
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, labelPtx, labelPty + lineHeight)
+ }
+ } else if (drawXOutside) {
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, labelPtx, labelPty + lineHeight / 2f)
+ }
+ } else if (drawYOutside) {
+ drawValue(
+ c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2f, dataSet
+ .getValueTextColor(j)
+ )
+ }
+ }
+ if (drawXInside || drawYInside) {
+ // calculate the text position
+ val x = labelRadius * sliceXBase + center.x
+ val y = labelRadius * sliceYBase + center.y
+ paintValues.textAlign = Paint.Align.CENTER
+
+ // draw everything, depending on settings
+ if (drawXInside && drawYInside) {
+ drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j))
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, x, y + lineHeight)
+ }
+ } else if (drawXInside) {
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, x, y + lineHeight / 2f)
+ }
+ } else if (drawYInside) {
+ drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j))
+ }
+ }
+ if (entry.icon != null && dataSet.isDrawIcons) {
+ val icon = entry.icon
+ val x = (labelRadius + iconsOffset.y) * sliceXBase + center.x
+ var y = (labelRadius + iconsOffset.y) * sliceYBase + center.y
+ y += iconsOffset.x
+ icon?.let { ic ->
+ c.drawImage(
+ ic, x.toInt(), y.toInt()
+ )
+ }
+ }
+ }
+ xIndex--
+ }
+ PointF.recycleInstance(iconsOffset)
+ }
+ }
+ PointF.recycleInstance(center)
+ c.restore()
+ }
+
+ private fun drawValuesNotTopAlign(c: Canvas) {
+ val rect = Rect()
+ paintEntryLabels.getTextBounds(text, 0, text.length, rect)
+ val textHeight = rect.height()
+ val center = chart.centerCircleBox
+
+ // get whole the radius
+ val radius = chart.radius
+ val rotationAngle = chart.rotationAngle
+ val drawAngles = chart.drawAngles
+ val absoluteAngles = chart.absoluteAngles
+ val phaseX = animator.phaseX
+ val phaseY = animator.phaseY
+ val holeRadiusPercent = chart.holeRadius / 100f
+ var labelRadiusOffset = radius / 10f * 3.6f
+ if (chart.isDrawHoleEnabled) {
+ labelRadiusOffset = radius - radius * holeRadiusPercent / 2f
+ }
+ val labelRadius = radius - labelRadiusOffset
+ val data = chart.getData()
+ val dataSets = data?.dataSets
+ val yValueSum = data?.yValueSum ?: 0F
+ val drawEntryLabels = chart.isDrawEntryLabelsEnabled
+ var angle: Float
+ var xIndex = 0
+ c.save()
+ val offset = 5f.convertDpToPixel()
+ dataSets?.let {
+ for (i in it.indices) {
+ val dataSet = dataSets[i]
+ val drawValues = dataSet.isDrawValues
+ if (!drawValues && !drawEntryLabels) continue
+ val xValuePosition = dataSet.xValuePosition
+ val yValuePosition = dataSet.yValuePosition
+
+ // apply the text-styling defined by the DataSet
+ applyValueTextStyle(dataSet)
+ val lineHeight = (paintValues.calcTextHeight("Q")
+ + 4f.convertDpToPixel())
+ val formatter = dataSet.valueFormatter
+ val entryCount = dataSet.entryCount
+ valueLinePaint.color = dataSet.valueLineColor
+ valueLinePaint.strokeWidth = dataSet.valueLineWidth.convertDpToPixel()
+ val sliceSpace = getSliceSpace(dataSet)
+ val iconsOffset = PointF.getInstance(dataSet.iconsOffset)
+ iconsOffset.x = iconsOffset.x.convertDpToPixel()
+ iconsOffset.y = iconsOffset.y.convertDpToPixel()
+ var lastPositionOfLeft = 0f
+ var lastPositionOfRight = 0f
+ for (j in 0 until entryCount) {
+ dataSet.getEntryForIndex(j)?.let { entry ->
+ angle = if (xIndex == 0) 0f else absoluteAngles[xIndex - 1] * phaseX
+ val sliceAngle = drawAngles[xIndex]
+ val sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius)
+
+ // offset needed to center the drawn text in the slice
+ val angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2f) / 2f
+ angle += angleOffset
+ val transformedAngle = rotationAngle + angle * phaseY
+ val value: Float = if (chart.isUsePercentValuesEnabled)
+ entry.y / yValueSum * 100f
+ else
+ entry.y
+ val sliceXBase = cos((transformedAngle * Utils.FDEG2RAD).toDouble()).toFloat()
+ val sliceYBase = sin((transformedAngle * Utils.FDEG2RAD).toDouble()).toFloat()
+ val drawXOutside = drawEntryLabels &&
+ xValuePosition == ValuePosition.OUTSIDE_SLICE
+ val drawYOutside = drawValues &&
+ yValuePosition == ValuePosition.OUTSIDE_SLICE
+ val drawXInside = drawEntryLabels &&
+ xValuePosition == ValuePosition.INSIDE_SLICE
+ val drawYInside = drawValues &&
+ yValuePosition == ValuePosition.INSIDE_SLICE
+ if (drawXOutside || drawYOutside) {
+ val valueLineLength1 = dataSet.valueLinePart1Length
+ dataSet.valueLinePart2Length
+ val valueLinePart1OffsetPercentage = dataSet.valueLinePart1OffsetPercentage / 100f
+ var pt2x: Float
+ var pt2y: Float
+ var labelPtx: Float
+ var labelPty: Float
+ val line1Radius: Float = if (chart.isDrawHoleEnabled) (radius - radius * holeRadiusPercent
+ * valueLinePart1OffsetPercentage) + radius * holeRadiusPercent else radius * valueLinePart1OffsetPercentage
+
+ val pt0x = line1Radius * sliceXBase + center.x
+ val pt0y = line1Radius * sliceYBase + center.y
+ val pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x
+ val pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y
+ if (transformedAngle % 360.0 in 90.0..270.0) {
+ pt2x = center.x - radius - 5
+ pt2y = if (lastPositionOfLeft == 0f) {
+ pt1y
+ } else {
+ if (lastPositionOfLeft - pt1y < textHeight) {
+ pt1y - (textHeight - (lastPositionOfLeft - pt1y))
+ } else {
+ pt1y
+ }
+ }
+ lastPositionOfLeft = pt2y
+ paintValues.textAlign = Paint.Align.RIGHT
+ if (drawXOutside) paintEntryLabels.textAlign = Paint.Align.RIGHT
+ labelPtx = pt2x - offset
+ labelPty = pt2y
+ } else {
+ pt2x = center.x + radius + 5
+ pt2y = if (lastPositionOfRight == 0f) {
+ pt1y
+ } else {
+ if (pt1y - lastPositionOfRight < textHeight) {
+ pt1y + (textHeight - (pt1y - lastPositionOfRight))
+ } else {
+ pt1y
+ }
+ }
+ lastPositionOfRight = pt2y
+ paintValues.textAlign = Paint.Align.LEFT
+ if (drawXOutside) paintEntryLabels.textAlign = Paint.Align.LEFT
+ labelPtx = pt2x + offset
+ labelPty = pt2y
+ }
+ if (dataSet.valueLineColor != ColorTemplate.COLOR_NONE) {
+ c.drawLine(pt0x, pt0y, pt1x, pt1y, valueLinePaint)
+ c.drawLine(pt1x, pt1y, pt2x, pt2y, valueLinePaint)
+ }
+
+ // draw everything, depending on settings
+ if (drawXOutside && drawYOutside) {
+ drawValue(
+ c,
+ formatter,
+ value,
+ entry,
+ 0,
+ labelPtx,
+ labelPty,
+ dataSet.getValueTextColor(j)
+ )
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, labelPtx, labelPty + lineHeight)
+ }
+ } else if (drawXOutside) {
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, labelPtx, labelPty + lineHeight / 2f)
+ }
+ } else if (drawYOutside) {
+ drawValue(
+ c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2f, dataSet
+ .getValueTextColor(j)
+ )
+ }
+ }
+ if (drawXInside || drawYInside) {
+ // calculate the text position
+ val x = labelRadius * sliceXBase + center.x
+ val y = labelRadius * sliceYBase + center.y
+ paintValues.textAlign = Paint.Align.CENTER
+
+ // draw everything, depending on settings
+ if (drawXInside && drawYInside) {
+ drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j))
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, x, y + lineHeight)
+ }
+ } else if (drawXInside) {
+ if (j < data.entryCount && entry.label != null) {
+ drawEntryLabel(c, entry.label!!, x, y + lineHeight / 2f)
+ }
+ } else if (drawYInside) {
+ drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j))
+ }
+ }
+ if (entry.icon != null && dataSet.isDrawIcons) {
+ val icon = entry.icon
+ val x = (labelRadius + iconsOffset.y) * sliceXBase + center.x
+ var y = (labelRadius + iconsOffset.y) * sliceYBase + center.y
+ y += iconsOffset.x
+ icon?.let { ic ->
+ c.drawImage(
+ ic, x.toInt(), y.toInt()
+ )
+ }
+ }
+ }
+ xIndex++
+ }
+ PointF.recycleInstance(iconsOffset)
+ }
+ }
+ PointF.recycleInstance(center)
+ c.restore()
+ }
+}
\ No newline at end of file