nglauber
nglauber

Reputation: 23894

MPAndroidChart PieChart label on top of the value

In MPAndroidChart pie chart, is it possible to show the labels on top of the values?

enter image description here

Upvotes: 3

Views: 1940

Answers (2)

MariosP
MariosP

Reputation: 9113

You can define a custom CustomPieChartRenderer which is a subclass of PieChartRenderer and override the below two methods we are interested to:

override fun drawEntryLabel(c: Canvas?, label: String, x: Float, y: Float)
override fun drawValue(c: Canvas?, valueText: String, x: Float, y: Float, color: Int)

The drawEntryLabel function is called when an entry label is ready to be drawn on Canvas at the specified x, y position.

The drawValue function is called when a value is ready to be drawn on Canvas at the specified x, y position.

Each of the above superclass functions draws a Text on Canvas using a Paint like c.drawText(label, x, y, mEntryLabelsPaint) or c.drawText(valueText, x, y, mValuePaint). When a label-value pair is ready to be drawn the above functions are called sequentially. So in the CustomPieChartRenderer when each function is called instead of calling superclass to draw the Text we save each data (value/label) temporary and we draw them when we have both label/value x, y positions. To align the label on top of the value we need only to swap the labelY position with valueY position during the drawing phase of each Text.

Below is the CustomPieChartRenderer:

class CustomPieChartRenderer(chart: PieChart?, animator: ChartAnimator?, viewPortHandler: ViewPortHandler?) : PieChartRenderer(chart, animator, viewPortHandler) {

    private var mHasLabelData = false
    private var mHasValueData = false
    private var mEntryLabelCanvas: Canvas? = null
    private var mValueCanvas: Canvas? = null
    private var mEntryLabel: String = ""
    private var mValueText: String = ""
    private var mEntryLabelX = 0f
    private var mValueX = 0f
    private var mEntryLabelY = 0f
    private var mValueY = 0f
    private var mValueColor = 0

    override fun drawEntryLabel(c: Canvas?, label: String, x: Float, y: Float) {
        //instead of calling super save the label data temporary
        //super.drawEntryLabel(c, label, x, y)
        mHasLabelData = true
        //save all entry label information temporary
        mEntryLabelCanvas = c
        mEntryLabel = label
        mEntryLabelX = x
        mEntryLabelY = y
        //and check if we have both label and value data temporary to draw them
        checkToDrawLabelValue()
    }

    override fun drawValue(c: Canvas?, valueText: String, x: Float, y: Float, color: Int) {
        //instead of calling super save the value data temporary
        //super.drawValue(c, valueText, x, y, color)
        mHasValueData = true
        //save all value information temporary
        mValueCanvas = c
        mValueText = valueText
        mValueX = x
        mValueY = y
        mValueColor = color
        //and check if we have both label and value data temporary to draw them
        checkToDrawLabelValue()
    }

    private fun checkToDrawLabelValue() {
        if (mHasLabelData && mHasValueData) {
            drawLabelAndValue()
            mHasLabelData = false
            mHasValueData = false
        }
    }

    private fun drawLabelAndValue() {
        //to show label on top of the value just swap the mEntryLabelY with mValueY
        drawEntryLabelData(mEntryLabelCanvas, mEntryLabel, mEntryLabelX, mValueY)
        drawValueData(mValueCanvas, mValueText, mValueX, mEntryLabelY, mValueColor)
    }

    //This is the same code used in super.drawEntryLabel(c, label, x, y) with any other customization you want in mEntryLabelsPaint
    private fun drawEntryLabelData(c: Canvas?, label: String, x: Float, y: Float) {
        val mEntryLabelsPaint: Paint = paintEntryLabels
        mEntryLabelsPaint.setColor(Color.BLACK)
        mEntryLabelsPaint.setTypeface(Typeface.DEFAULT_BOLD)
        mEntryLabelsPaint.setTextAlign(Paint.Align.CENTER)
        c?.drawText(label, x, y, mEntryLabelsPaint)
    }

    //This is the same code used in super.drawValue(c, valueText, x, y, color) with any other customization you want in mValuePaint
    fun drawValueData(c: Canvas?, valueText: String, x: Float, y: Float, color: Int) {
        mValuePaint.color = color
        mValuePaint.textAlign = Paint.Align.CENTER
        c?.drawText(valueText, x, y, mValuePaint)
    }
}

And here is a sample usage:

class PieChartActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.piechart_layout)

        val pieChart = findViewById<PieChart>(R.id.pieChart)

        //initialize a List of colors one for each slice
        val pieColors: ArrayList<Int> = ArrayList()
        pieColors.add(Color.parseColor("#0f2c4b"))
        pieColors.add(Color.parseColor("#1857a0"))
        pieColors.add(Color.parseColor("#238837"))
        pieColors.add(Color.parseColor("#3f9cff"))

        //initialize a List of PieEntry with its value/label pair
        val pieEntries: ArrayList<PieEntry> = ArrayList()
        pieEntries.add(PieEntry(40f, "NA"))
        pieEntries.add(PieEntry(18f, "MENA"))
        pieEntries.add(PieEntry(20f, "EU"))
        pieEntries.add(PieEntry(22f, "ASIA"))

        //prepare the PieDataSet with the above pieEntries and pieColors
        val pieDataSet = PieDataSet(pieEntries, "")
        pieDataSet.valueTextSize = 14f
        pieDataSet.colors = pieColors

        //draw value/label outside the pie chart
        pieDataSet.xValuePosition = PieDataSet.ValuePosition.OUTSIDE_SLICE
        pieDataSet.yValuePosition = PieDataSet.ValuePosition.OUTSIDE_SLICE
        pieDataSet.valueLinePart1OffsetPercentage = 100f
        pieDataSet.valueLinePart1Length = 0.8f
        pieDataSet.valueLinePart2Length = 0f
        pieDataSet.valueTextColor = Color.BLACK
        pieDataSet.valueTypeface = Typeface.DEFAULT_BOLD
        pieDataSet.valueLineColor = ColorTemplate.COLOR_NONE

        //prepare the PieData
        val pieData = PieData(pieDataSet)
        pieData.setValueTextColor(Color.BLACK)
        pieData.setDrawValues(true)
        pieData.setValueFormatter(PercentFormatter(pieChart))

        //set pieChart data and any other pieChart property needed
        pieChart.data = pieData
        pieChart.setExtraOffsets(35f, 35f, 35f, 35f)
        pieChart.setEntryLabelColor(Color.BLACK)
        pieChart.setEntryLabelTextSize(14f)
        pieChart.setEntryLabelTypeface(Typeface.DEFAULT_BOLD)
        pieChart.setUsePercentValues(true)
        pieChart.legend.isEnabled = false
        pieChart.description.isEnabled = false
        pieChart.isRotationEnabled = true
        pieChart.dragDecelerationFrictionCoef = 0.9f
        pieChart.rotationAngle = 220f
        pieChart.isHighlightPerTapEnabled = true
        pieChart.animateY(1400, Easing.EaseInOutQuad)
        pieChart.setHoleColor(Color.WHITE)

        //set the custom renderer (CustomPieChartRenderer) used to draw each label on top of the value and call invalidate to redraw the chart
        pieChart.renderer = CustomPieChartRenderer(pieChart, pieChart.animator, pieChart.viewPortHandler)
        pieChart.invalidate()
    }
}

Piechart Xml Layout :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white">

    <com.github.mikephil.charting.charts.PieChart
        android:id="@+id/pieChart"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

Result:

piechart

Note: This is tested with 'com.github.PhilJay:MPAndroidChart:v3.1.0'

Upvotes: 2

user16930239
user16930239

Reputation: 9727

As seen in documentation there is an Enum called PieDataSet.ValuePosition have a constant OUTSIDE_SLICE

And if you check Pie Chart Renderer in the source code you will fin these values drawXOutside drawYOutside xValuePosition yValuePosition which indicate that in the worse case scenario you could edit the source code to define the position. Also in the source code you will find transformedAngle you could set it to 0 if you want the labels to be not rotated at all.

Upvotes: 0

Related Questions