Reputation: 23894
In MPAndroidChart pie chart, is it possible to show the labels on top of the values?
Upvotes: 3
Views: 1940
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:
Note: This is tested with 'com.github.PhilJay:MPAndroidChart:v3.1.0'
Upvotes: 2
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