MPAndroidChart - 饼图 - 自定义标签线

4

我正在尝试使用MPAndroidChart绘制饼图中的标签线,如下图所示。但我无法弄清楚如何:

  1. 将标签线与图表分离
  2. 在线条开始处绘制小圆圈。

enter image description here

谢谢。

1个回答

7

这并不容易实现。为了将线条与图表分离,可以使用valueLinePart1OffsetPercentage并调整线段长度。但要让图表在线段末端绘制圆点,则需要自定义渲染器。下面是一个:

class CustomPieChartRenderer(pieChart: PieChart, val circleRadius: Float)
    : PieChartRenderer(pieChart, pieChart.animator, pieChart.viewPortHandler) {

    override fun drawValues(c: Canvas) {
        super.drawValues(c)

        val center = mChart.centerCircleBox

        val radius = mChart.radius
        var rotationAngle = mChart.rotationAngle
        val drawAngles = mChart.drawAngles
        val absoluteAngles = mChart.absoluteAngles

        val phaseX = mAnimator.phaseX
        val phaseY = mAnimator.phaseY

        val roundedRadius = (radius - radius * mChart.holeRadius / 100f) / 2f
        val holeRadiusPercent = mChart.holeRadius / 100f
        var labelRadiusOffset = radius / 10f * 3.6f

        if (mChart.isDrawHoleEnabled) {
            labelRadiusOffset = (radius - radius * holeRadiusPercent) / 2f
            if (!mChart.isDrawSlicesUnderHoleEnabled && mChart.isDrawRoundedSlicesEnabled) {
                rotationAngle += roundedRadius * 360 / (Math.PI * 2 * radius).toFloat()
            }
        }

        val labelRadius = radius - labelRadiusOffset

        val dataSets = mChart.data.dataSets

        var angle: Float
        var xIndex = 0

        c.save()
        for (i in dataSets.indices) {
            val dataSet = dataSets[i]
            val sliceSpace = getSliceSpace(dataSet)
            for (j in 0 until dataSet.entryCount) {
                angle = if (xIndex == 0) 0f else absoluteAngles[xIndex - 1] * phaseX
                val sliceAngle = drawAngles[xIndex]
                val sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius)
                angle += (sliceAngle - sliceSpaceMiddleAngle / 2f) / 2f

                if (dataSet.valueLineColor != ColorTemplate.COLOR_NONE) {
                    val transformedAngle = rotationAngle + angle * phaseY
                    val sliceXBase = cos(transformedAngle * Utils.FDEG2RAD.toDouble()).toFloat()
                    val sliceYBase = sin(transformedAngle * Utils.FDEG2RAD.toDouble()).toFloat()
                    val valueLinePart1OffsetPercentage = dataSet.valueLinePart1OffsetPercentage / 100f
                    val line1Radius = if (mChart.isDrawHoleEnabled) {
                        (radius - radius * holeRadiusPercent) * valueLinePart1OffsetPercentage + radius * holeRadiusPercent
                    } else {
                        radius * valueLinePart1OffsetPercentage
                    }
                    val px = line1Radius * sliceXBase + center.x
                    val py = line1Radius * sliceYBase + center.y

                    if (dataSet.isUsingSliceColorAsValueLineColor) {
                        mRenderPaint.color = dataSet.getColor(j)
                    }
                    c.drawCircle(px, py, circleRadius, mRenderPaint)
                }

                xIndex++
            }
        }
        MPPointF.recycleInstance(center)
        c.restore()
    }
}

这个自定义渲染器扩展了默认的饼图渲染器。我基本上只是从 PieChartRenderer.drawValues 方法中复制了代码,转换为Kotlin,并删除了不需要的内容。我只保留了需要确定线末端点位置的逻辑。

我试着重新创建你展示的那张图片:


val chart: PieChart = view.findViewById(R.id.pie_chart)
chart.setExtraOffsets(40f, 0f, 40f, 0f)

// Custom renderer used to add dots at the end of value lines.
chart.renderer = CustomPieChartRenderer(chart, 10f)

val dataSet = PieDataSet(listOf(
        PieEntry(40f),
        PieEntry(10f),
        PieEntry(10f),
        PieEntry(15f),
        PieEntry(10f),
        PieEntry(5f),
        PieEntry(5f),
        PieEntry(5f)
), "Pie chart")

// Chart colors
val colors = listOf(
        Color.parseColor("#4777c0"),
        Color.parseColor("#a374c6"),
        Color.parseColor("#4fb3e8"),
        Color.parseColor("#99cf43"),
        Color.parseColor("#fdc135"),
        Color.parseColor("#fd9a47"),
        Color.parseColor("#eb6e7a"),
        Color.parseColor("#6785c2"))
dataSet.colors = colors
dataSet.setValueTextColors(colors)

// Value lines
dataSet.valueLinePart1Length = 0.6f
dataSet.valueLinePart2Length = 0.3f
dataSet.valueLineWidth = 2f
dataSet.valueLinePart1OffsetPercentage = 115f  // Line starts outside of chart
dataSet.isUsingSliceColorAsValueLineColor = true

// Value text appearance
dataSet.yValuePosition = PieDataSet.ValuePosition.OUTSIDE_SLICE
dataSet.valueTextSize = 16f
dataSet.valueTypeface = Typeface.DEFAULT_BOLD

// Value formatting
dataSet.valueFormatter = object : ValueFormatter() {
    private val formatter = NumberFormat.getPercentInstance()

    override fun getFormattedValue(value: Float) =
            formatter.format(value / 100f)
}
chart.setUsePercentValues(true)

dataSet.selectionShift = 3f

// Hole
chart.isDrawHoleEnabled = true
chart.holeRadius = 50f

// Center text
chart.setDrawCenterText(true)
chart.setCenterTextSize(20f)
chart.setCenterTextTypeface(Typeface.DEFAULT_BOLD)
chart.setCenterTextColor(Color.parseColor("#222222"))
chart.centerText = "Center\ntext"

// Disable legend & description
chart.legend.isEnabled = false
chart.description = null

chart.data = PieData(dataSet)

再次说明,这并不是很直观。我希望你喜欢Kotlin!如果你经常需要它,你可以将大部分配置代码移到子类中。以下是结果:

输入图像描述

我不是MPAndroidChart专家。实际上,我只用过一次,那是两年前的事了。但如果你做好了调研工作,大多数时候都会找到解决方案。幸运的是,MPAndroidChart非常具有可定制性。


2
谢谢。不太喜欢 Kotlin :)抱歉。 - ALex
1
Kotlin 已成为 Android 官方语言一段时间了!但转换到 Java 不应该太难。 - Nicolas
dataSet.setValueTextColors(colors) - Nicolas
@Nicolas 很好的回答。你能看一下我的问题吗?https://stackoverflow.com/q/68190761/6444297 - Alireza Noorali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接