Trying to implement mpandroidchart, but the graph is ending up useless because it is plotting the chart incorrectly.
Below is the snippet of how i try to implement.
fun setGraph(cdata : ArrayList<Entry>){
val lineChart: LineChart = bind.lineChartViewPage
lineChart.setExtraOffsets(0f, 40f, 0f, 40f)
lineChart.setDrawBorders(false)
lineChart.setDrawGridBackground(false)
lineChart.legend.isEnabled = false
lineChart.description.isEnabled = false
lineChart.setTouchEnabled(false)
lineChart.isDragEnabled = false
lineChart.setScaleEnabled(false)
lineChart.setPinchZoom(false)
lineChart.isAutoScaleMinMaxEnabled = false
val lds = LineDataSet(cdata, "my data")
lds.mode = LineDataSet.Mode.LINEAR
lds.setDrawCircleHole(false)
lds.setDrawCircles(false)
lds.color = Color.parseColor("#ffffff")
lds.lineWidth = 1.5f
lds.isHighlightEnabled = false
val lineDataSets: ArrayList<ILineDataSet> = ArrayList<ILineDataSet>()
lineDataSets.add(lds)
lds.setDrawValues(false)
val lineData = LineData(lineDataSets)
val ll1 = LimitLine(2.5F)
ll1.lineWidth = 2f
ll1.lineColor = Color.parseColor("#99ce7aff")
val yAxisRight: YAxis = lineChart.axisRight
yAxisRight.textColor = Color.WHITE
yAxisRight.setDrawZeroLine(false)
yAxisRight.setDrawAxisLine(false)
yAxisRight.setCenterAxisLabels(true)
yAxisRight.setDrawTopYLabelEntry(true)
yAxisRight.setDrawGridLines(false)
yAxisRight.setGranularity(1.0f)
yAxisRight.isGranularityEnabled = true
yAxisRight.setLabelCount(6, true)
yAxisRight.removeAllLimitLines()
yAxisRight.addLimitLine(ll1)
yAxisRight.axisMaximum = 3.0f
yAxisRight.axisMinimum = 0.0f
val xAxis: XAxis = lineChart.xAxis
xAxis.isEnabled = false
xAxis.valueFormatter = null
xAxis.setDrawLabels(false)
xAxis.setDrawGridLines(false)
val yAxisLeft: YAxis = lineChart.axisLeft
yAxisLeft.isEnabled = false
yAxisLeft.setDrawLabels(false)
yAxisLeft.setDrawGridLines(false)
// hide legend
val legend: Legend = lineChart.legend
legend.isEnabled = false
lineChart.data = lineData
lineChart.data.notifyDataChanged()
lineChart.notifyDataSetChanged()
lineChart.invalidate()
}
I tried out some combinations and found a different behavior if i comment out below lines :
yAxisRight.axisMaximum = 3.0f
yAxisRight.axisMinimum = 0.0f
Before commenting lines (incorrect plotting with limit line)
After commenting lines (correct plotting and limit line vanished)
Below is the dataset on which the graph is being plotted. here the max value is 0.8f and min value is 0.1f.
private fun fakeData():ArrayList<Entry>{
val al: ArrayList<Entry> = ArrayList()
al.add(Entry(1F, 0.2F))
al.add(Entry(2F, 0.4F))
al.add(Entry(3F, 0.5F))
al.add(Entry(4F, 0.6F))
al.add(Entry(5F, 0.3F))
al.add(Entry(6F, 0.4F))
al.add(Entry(7F, 0.6F))
al.add(Entry(8F, 0.7F))
al.add(Entry(9F, 0.4F))
al.add(Entry(10F, 0.1F))
al.add(Entry(11F, 0.3F))
al.add(Entry(12F, 0.3F))
al.add(Entry(13F, 0.5F))
al.add(Entry(14F, 0.6F))
al.add(Entry(15F, 0.47F))
al.add(Entry(16F, 0.55F))
al.add(Entry(17F, 0.66F))
al.add(Entry(18F, 0.45F))
al.add(Entry(19F, 0.46F))
al.add(Entry(20F, 0.33F))
al.add(Entry(21F, 0.4F))
al.add(Entry(22F, 0.36F))
al.add(Entry(23F, 0.37F))
al.add(Entry(24F, 0.46F))
al.add(Entry(25F, 0.74F))
al.add(Entry(26F, 0.76F))
al.add(Entry(27F, 0.79F))
al.add(Entry(28F, 0.8F))
return al
}
If i dont set min and max values as below; then the limit line won't show up.
yAxisRight.axisMaximum = 3.0f
yAxisRight.axisMinimum = 0.0f
chart version : com.github.PhilJay:MPAndroidChart:v3.1.0
Please let me know if there is something wrong with this implementation or possible ways to correct this.
UPDATE :
Tried multiple dataset and found that defining custom yaxis min and max value is causing the chart to be drawn incorrectly. I cant understand why?
Had the same issue and the fix is pretty easy, but not straightforward, though, you should set the same max and min values for both y-axises:
axisLeft.apply {
axisMinimum = 0.0f
axisMaximum = 3.0f
}
axisRight.apply {
axisMinimum = 0.0f
axisMaximum = 3.0f
}
Related
I'm creating multi bar chart using MPAndroidChart library for android, but it's not as per our design requirement,
I can able to create like following image using following code
barChart.description.isEnabled = false
barChart.setPinchZoom(false)
barChart.setDrawBarShadow(false)
barChart.setDrawGridBackground(false)
val mv = MyMarkerView(context, R.layout.view_marker)
mv.chartView = barChart
barChart.marker = mv
val l: Legend = barChart.legend
l.isEnabled = false
barChart.setExtraOffsets(5f, 5f, 5f, 15f)
val xAxis: XAxis = barChart.xAxis
xAxis.granularity = 1f
xAxis.setCenterAxisLabels(true)
xAxis.position = XAxis.XAxisPosition.BOTTOM
val leftAxis: YAxis = barChart.axisLeft
leftAxis.valueFormatter = MyYAxisValueFormatter(barChart)
leftAxis.setDrawGridLines(false)
leftAxis.spaceTop = 35f
leftAxis.axisMinimum = 0f
barChart.axisRight.isEnabled = false
barChart.axisLeft.setDrawGridLines(false)
barChart.xAxis.setDrawGridLines(false)
and set data for bar char as per code below
val values1 = ArrayList<BarEntry>()
val values2 = ArrayList<BarEntry>()
for ((index, value) in dataList.withIndex()) {
values1.add(BarEntry((index).toFloat(), value.y1.toFloat()))
values2.add(BarEntry((index).toFloat(), value.y2.toFloat()))
}
Log.d("data", "sales: ${values1.size} / purchase: ${values2.size}")
val set1 = BarDataSet(values1, "Sales")
val set2 = BarDataSet(values2, "Purchase")
set1.color = colorSales
set2.color = colorPurchase
val data = BarData(set1, set2)
data.setValueFormatter(LargeValueFormatter())
data.setDrawValues(false)
barChart.data = data
val months = arrayOf("APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", "JAN", "FEB", "MAR")
barChart.xAxis.valueFormatter = IndexAxisValueFormatter(months)
val xAxis = barChart.xAxis
xAxis.valueFormatter = IndexAxisValueFormatter(months)
barChart.axisLeft.axisMinimum = 0f
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.granularity = 1f
xAxis.setCenterAxisLabels(true)
xAxis.isGranularityEnabled = true
val barSpace = 0.1f
val groupSpace = 0.3f
val groupCount = 12
data.barWidth = 0.2f
barChart.xAxis.axisMinimum = 0f
barChart.xAxis.axisMaximum = 0 + barChart.barData.getGroupWidth(groupSpace, barSpace) * groupCount
barChart.xAxis.xOffset = -10f
barChart.groupBars(0.7f, groupSpace, barSpace) // perform the "explicit" grouping
barChart.invalidate()
output of above code is chart below
but i want graph like below,
please help me on this if anyone has experience with using MPAndroidChart
You need to make xAxis.isGranularityEnabled = false
From the documentation:
* When true, axis labels are controlled by the granularity property.
* When false, axis values could possibly be repeated.
* This could happen if two adjacent axis values are rounded to same
value.
* If using granularity this could be avoided by having
fewer axis values visible
I try to display some charts in my android app. I use MPAndroidChart library. But somehow my chart is always compress on the x-axis. The value seems to have all the x value 0.
In the data set this is not the case. I can not figure out what I am doing wrong.
val chart = binding.statsGraph
chart.description.isEnabled = false
chart.setDrawGridBackground(false)
chart.setDrawBarShadow(false)
chart.isHighlightFullBarEnabled = false
chart.setBackgroundColor(Color.WHITE)
chart.setDrawGridBackground(false)
chart.setDrawBarShadow(false)
chart.setHighlightFullBarEnabled(false)
// draw bars behind lines
chart.setDrawOrder(
arrayOf(
CombinedChart.DrawOrder.BAR,
CombinedChart.DrawOrder.BUBBLE,
CombinedChart.DrawOrder.CANDLE,
CombinedChart.DrawOrder.LINE,
CombinedChart.DrawOrder.SCATTER
)
)
val legend = chart.getLegend()
legend.isWordWrapEnabled = true
legend.verticalAlignment = Legend.LegendVerticalAlignment.BOTTOM
legend.horizontalAlignment = Legend.LegendHorizontalAlignment.CENTER
legend.orientation = Legend.LegendOrientation.HORIZONTAL
legend.setDrawInside(false)
val rightAxis = chart.getAxisRight()
rightAxis.setDrawGridLines(false)
rightAxis.axisMinimum = 0f // this replaces setStartAtZero(true)
val leftAxis = chart.getAxisLeft()
leftAxis.setDrawGridLines(false)
leftAxis.axisMinimum = 0f // this replaces setStartAtZero(true)
val xAxis = chart.getXAxis()
xAxis.position = XAxis.XAxisPosition.BOTH_SIDED
xAxis.axisMinimum = 0f
xAxis.granularity = 1f
class MyValueFormatter : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return value.toInt().toString()
}
}
xAxis.valueFormatter = MyValueFormatter()
val data = CombinedData()
data.setData(test())
xAxis.axisMaximum = data.xMax + 0.25f
chart.setData(data)
chart.invalidate()
You have to make sure that your layout and its parent layout has a "match_parent" width.
I'm implementing a feature for displaying Target & Actual for properties .
So, I have to display Target labels according to filter selected for example if 3 Month filter selected then need to display March , Feb & Jan but what currently labels are not displaying on XAxis.
Here is my code , please rectify any issue:
fun showOrderChart(){
try {
orderChart.description.isEnabled=false
orderChart.setMaxVisibleValueCount(60)
// scaling can now only be done on x- and y-axis separately
// scaling can now only be done on x- and y-axis separately
orderChart.setPinchZoom(true)
orderChart.setDrawBarShadow(false)
orderChart.setDrawGridBackground(false)
/* val xAxis: XAxis = orderChart.xAxis
xAxis.position = XAxisPosition.BOTTOM
xAxis.setDrawGridLines(false)*/
orderChart.axisLeft.setDrawGridLines(false)
// add a nice and smooth animation
// add a nice and smooth animation
orderChart.animateY(1500)
orderChart.legend.isEnabled = false
val xVals = ArrayList<String>()
xVals.add("Jan")
xVals.add("Feb")
xVals.add("Mar")
xVals.add("Apr")
xVals.add("May")
xVals.add("Jun")
val yVals1 = ArrayList<BarEntry>()
val yVals2 = ArrayList<BarEntry>()
yVals1.add(BarEntry(1f, 1.toFloat()))
yVals2.add(BarEntry(1f, 2.toFloat()))
yVals1.add(BarEntry(2f, 3.toFloat()))
yVals2.add(BarEntry(2f, 4.toFloat()))
yVals1.add(BarEntry(3f, 5.toFloat()))
yVals2.add(BarEntry(3f, 6.toFloat()))
yVals1.add(BarEntry(4f, 7.toFloat()))
yVals2.add(BarEntry(4f, 5.toFloat()))
yVals1.add(BarEntry(5f, 9.toFloat()))
yVals2.add(BarEntry(5f, 12.toFloat()))
yVals1.add(BarEntry(6f, 11.toFloat()))
yVals2.add(BarEntry(6f, 8.toFloat()))
val set1:BarDataSet
val set2:BarDataSet
set1 = BarDataSet(yVals1, "Target")
set1.color = Color.RED
set2 = BarDataSet(yVals2, "Actual")
set2.color = Color.BLUE
val data = BarData(set1, set2)
data.setValueFormatter(LargeValueFormatter())
orderChart.data = data
orderChart.getBarData().setBarWidth(0.3f)
orderChart.xAxis.setAxisMinimum(0f)
orderChart.getXAxis().setAxisMaximum(0 + orderChart.getBarData().getGroupWidth(0.4f, 0f) * 6)
orderChart.groupBars(0f, 0.4f, 0f)
orderChart.getData().setHighlightEnabled(false)
orderChart.invalidate()
val l = orderChart.getLegend()
l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP)
l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT)
l.setOrientation(Legend.LegendOrientation.VERTICAL)
l.setDrawInside(true)
l.setYOffset(20f)
l.setXOffset(0f)
l.setYEntrySpace(0f)
l.setTextSize(8f)
val xAxis = orderChart.getXAxis()
xAxis.granularity = 1f
xAxis.setGranularityEnabled(true)
xAxis.setCenterAxisLabels(true)
xAxis.setDrawGridLines(false)
xAxis.setAxisMaximum(6f)
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM)
xAxis.setValueFormatter(IndexAxisValueFormatter(xVals))
orderChart.getAxisRight().setEnabled(false)
val leftAxis = orderChart.getAxisLeft()
leftAxis.setValueFormatter(LargeValueFormatter())
leftAxis.setDrawGridLines(true)
leftAxis.setSpaceTop(35f)
leftAxis.setAxisMinimum(0f)
}catch (e: Exception){
e.printStackTrace()
}
}
you need code for label. like this.
YAxis yAxisRight = lineChart.getAxisRight(); //set right of Y
yAxisRight.setDrawLabels(true);
I have a problem updating the data in a chart. When I change the settings and come back to this fragment or when I go out of the app and come back, the chart apparently changes its size and hide the labels in its axis. However, if I navigate through the app and come back, the chart is displaying well.
Screenshot of correct display
Screenshot of axis labels disappearing
My context is the following:
I have a fragment where I observe the viewmodel in onViewCreated. When the data is successfully retrieved, I update the chart. I have tried also to make a flag to make sure that the call to set up the chart is only called once, but that doesn't work either. So the problem is not related to how many times this call is made.
viewModel.forecast.observe(viewLifecycleOwner, Observer { resource ->
if (resource != null && resource.status.isSuccessful()){
ChartUtilities.setUpChart(
viewModel.forecastOfNextHours().subList(0, 9),
binding.lineChart,
"Temperature of the next 24 hours",
isMetric
)
}
In ChartUtilities.kt:
object ChartUtilities {
fun setUpChart(list: List<ForecastListItem>, lineChart: LineChart, label: String, isMetric: Boolean){
//lineChart.clear()
val (values, yValues, xAxisValues) = fillData(list, isMetric)
val data = configureDataSet(values, label)
customSettings(lineChart, data, xAxisValues, yValues)
//lineChart.notifyDataSetChanged()
}
private fun configureDataSet(
values: ArrayList<Entry>,
label: String
): LineData {
val set1 = LineDataSet(values, label)
set1.axisDependency = YAxis.AxisDependency.LEFT
set1.color = ColorTemplate.getHoloBlue()
set1.valueTextColor = ColorTemplate.getHoloBlue()
set1.lineWidth = 4.0f
set1.setDrawCircles(false)
set1.setDrawValues(false)
set1.fillAlpha = 65
set1.fillColor = Color.RED
set1.highLightColor = Color.rgb(244, 117, 117)
set1.setDrawCircleHole(false)
set1.mode = LineDataSet.Mode.HORIZONTAL_BEZIER
// create a data object with the data sets
val data = LineData(set1)
data.setValueTextColor(Color.WHITE)
data.setValueTextSize(9f)
return data
}
private fun fillData(list: List<ForecastListItem>, isMetric: Boolean): Triple<ArrayList<Entry>, ArrayList<Double>, ArrayList<String>> {
val values = ArrayList<Entry>()
val yValues = ArrayList<Double>()
val xAxisValues = arrayListOf<String>()
list.forEachIndexed { index, forecastItem ->
xAxisValues.add(forecastItem.getHourOfDay())
var temperature = forecastItem.getTemperature()!!
if (!isMetric)
temperature = WeatherUtils.celsiusToFahrenheit(temperature)
yValues.add(temperature)
values.add(Entry(index.toFloat(), temperature.toFloat()))
}
return Triple(values, yValues, xAxisValues)
}
private fun customSettings(chart: LineChart, data: LineData, xAxisValues: ArrayList<String>, yAxisValues: ArrayList<Double>){
chart.data = data
customChart(chart)
customLegend(chart)
customXAxis(chart, xAxisValues)
customYAxis(chart, yAxisValues)
val rightAxis = chart.axisRight
rightAxis.isEnabled = false
}
private fun customChart(chart: LineChart) {
chart.description.isEnabled = false
chart.setTouchEnabled(false)
//chart.dragDecelerationFrictionCoef = 0.9f
// enable scaling and dragging
chart.isDragEnabled = false
chart.setScaleEnabled(false)
chart.setDrawGridBackground(false)
chart.isHighlightPerDragEnabled = true
// set an alternative background color
chart.setBackgroundResource(R.color.colorPrimaryDark)
chart.setViewPortOffsets(0f, 0f, 0f, 0f)
}
private fun customLegend(chart: LineChart) {
val l = chart.legend
l.isEnabled = true
l.verticalAlignment = Legend.LegendVerticalAlignment.TOP
l.textColor = Color.WHITE
}
private fun customXAxis(
chart: LineChart,
xAxisValues: ArrayList<String>
) {
val xAxis = chart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.textSize = 12f
xAxis.setDrawAxisLine(true)
xAxis.setDrawGridLines(false)
xAxis.textColor = Color.WHITE
//xAxis.setCenterAxisLabels(true)
xAxis.setAvoidFirstLastClipping(false)
xAxis.granularity = 2f // three hours
xAxis.axisMaximum = xAxisValues.size.toFloat()
xAxis.labelCount = xAxisValues.size
xAxis.valueFormatter = IndexAxisValueFormatter(xAxisValues)
xAxis.isEnabled = true
}
private fun customYAxis(
chart: LineChart,
yAxisValues: ArrayList<Double>
) {
val leftAxis = chart.axisLeft
leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
leftAxis.textColor = Color.WHITE
leftAxis.setDrawGridLines(true)
leftAxis.isGranularityEnabled = true
leftAxis.axisMinimum = yAxisValues.min()?.minus(10)?.toFloat()!!
leftAxis.axisMaximum = yAxisValues.max()?.plus(10)?.toFloat()!!
leftAxis.yOffset = -9f
leftAxis.isEnabled = true
}
}
I realise this is a very late answer, but I had exactly the same issue and stumbled upon your question. I'd intermittently see the axes disappear when scrolling between different charts (I had them in a RecyclerView).
I solved it by deleting the following line, which I see you also have in your code:
chart.setViewPortOffsets(0, 0, 0, 0);
The javadoc for that method says that we should avoid manually setting it unless we know what we're doing (which I don't!).
I also found that increasing the values to the following worked for me:
chart.setViewPortOffsets(60, 0, 50, 60);
I have a MPChart and I am setting the values like this
lineChart.legend.isEnabled = false
lineChart.description.isEnabled = false
val entryList = arrayListOf<Entry>()
val sortedList = stockGraphResponse?.prices?.sortedWith(compareBy {
it?.date
})
for (i in 0 until sortedList?.size!!) {
val item = Utils.replaceCommaInNumber(sortedList[i]?.price!!).toFloat()
entryList.add(Entry(sortedList[i]?.date?.toFloat()!!, item))
}
val lineDataSet = LineDataSet(entryList,"")
lineDataSet.setDrawCircles(false)
lineDataSet.setDrawFilled(true)
val lineData = LineData(lineDataSet)
lineChart.data = lineData
lineChart.setDrawBorders(true)
val xAxis = lineChart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.setValueFormatter(CustomFormatter())
xAxis.setLabelCount(5,true)
val yAxis = lineChart.axisLeft
val yRight = lineChart.axisRight
yRight.isEnabled = false
lineChart.isAutoScaleMinMaxEnabled = true
lineChart.invalidate()
And the result is this graph
I am setting 5 for label count in xAxis and I am getting 5 labels but then the values are being plotted like that and so they are overlapping. I tried using granularity but still the same. How can I streamline this? Any help would be appreciated. Thanks