I currently have 30 values in my line chart and currently the X axis is showing 01, 03, 05, 06.
How would i make it to show the X-Axis Like 01,02,03,04,05 with proper spacing between each x-axis
I'm currently using this method
float minXRange = 10;
float maxXRange = 10;
lineChart.setVisibleXRange(minXRange, maxXRange);
to show the values i want in my linechart
This works for me. You can try this. This will set the X-Axis values of the graph.
final String[] values = {"01", "02", "03"};
mChart.getXAxis().setValueFormatter(new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return values[(int)value];
}
});
You may need to set the granularity of the x-axis to 1f by doing getXAxis.setGranularity(1f)
I'm working on android MP combine chart. I'm passing real time data to chart using firebase. And I successfully done to get data and pass into combine chart.
But the problem is that when firebase updates itself from some other data source, chart is not updating itself according to values (got in data snapshot).
Rather than chat is moving forward but data is not updating as you can see here.
I saw many solutions but they are different according to requirement and chart type. How to solve my chart problem? It would great help.
Here's is my onDataChange() code. Calling in
onCreate
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
//get FLOW RATE
String rate = ds.child("flowRate").getValue(String.class);
//get TIMESTAMP
long timestamp = ds.child("timestamp").getValue(Long.class);
//add, got TIMESTAMP to xAxisData (list)
xAxisData.add(getDatafTimeStamp(timestamp));
//add, got FLOW RATE to flow_rate (list)
flow_rate.add(rate);
combinedChart.notifyDataSetChanged();
combinedChart.invalidate();
}
drawCombinedChart(xAxisData, flow_rate, quantity, followed);
#Override
public void onCancelled(DatabaseError databaseError) {
datafetched = false;
}
});
Here's my drawCombinechart() function, called every time when data updates in firebase
public void drawCombinedChart(List<String> axis, final List<String> rate) {
//set chart's description, grid, shadow when chart is loaded
combinedChart.setDescription("");
combinedChart.setDrawGridBackground(false);
combinedChart.setDrawBarShadow(false);
combinedChart.setNoDataText("Loading.....");
// draw bars behind lines
combinedChart.setDrawOrder(new CombinedChart.DrawOrder[]{
CombinedChart.DrawOrder.BAR, CombinedChart.DrawOrder.LINE, CombinedChart.DrawOrder.LINE
});
Legend l = combinedChart.getLegend();
l.setWordWrapEnabled(true);
// Setting X_Axis of combine chart
XAxis xAxis = combinedChart.getXAxis();
xAxis.setLabelsToSkip(0);
// Setting X_Axis position in bottom of combine chart
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); // to set xAxis in Bottom
//setting X_Axis only on right side
combinedChart.getAxisRight().setDrawLabels(true);
combinedChart.getAxisLeft().setDrawZeroLine(false);
combinedChart.getAxisRight().setAxisMinValue(0);
// Setting Y_Axis to left side of combine chart
YAxis y = combinedChart.getAxisLeft();
y.setAxisMinValue(0);
y.setGranularity(1f);
y.setGranularityEnabled(true);
y.setAxisLineColor(Color.BLACK);
y.setTextColor(Color.BLACK);
y.setEnabled(true);
y.setAxisLineColor(Color.BLACK);
y.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
y.setDrawGridLines(false);
y.setLabelCount(5, true);
y.setDrawZeroLine(false);
y.isForceLabelsEnabled();
y.setDrawLabels(true);
y.setDrawLimitLinesBehindData(true);
y.setDrawGridLines(true);
y.setDrawLimitLinesBehindData(true);
y.setTextColor(Color.RED);
y.setDrawZeroLine(true);
//animate Y_axis to 5 seconds
combinedChart.animateY(0);
// Setting Y_Axis to right side of combine chart
YAxis rightAxis = combinedChart.getAxisRight();
rightAxis.setDrawGridLines(true);
rightAxis.setLabelCount(5, true);
rightAxis.setDrawZeroLine(false);
rightAxis.isForceLabelsEnabled();
rightAxis.setDrawLabels(true);
rightAxis.setDrawLimitLinesBehindData(true);
rightAxis.setDrawGridLines(true);
rightAxis.setDrawLimitLinesBehindData(true);
rightAxis.setTextColor(Color.RED);
rightAxis.setDrawZeroLine(true);
//hide background grid lines of chart
combinedChart.getXAxis().setDrawGridLines(false);
combinedChart.getAxisLeft().setDrawGridLines(false);
combinedChart.getAxisRight().setDrawGridLines(false);
data = new CombinedData(axis);
//when data loaded show msg onto chart
combinedChart.setNoDataText("Data loaded");
//switch pressed
flw_Rate_switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//if FlOW RATE button is on
if (isChecked) {
//change state of other switches
lqd_followed_switch.setChecked(false);
//change color of current switch when ON
flow_rate_switch_txt.setTextColor(Color.parseColor("#536BB5"));
//call function to generate area chart
mlineData = generateLineData(rate);
data.setData(mlineData);
combinedChart.setData(data);
// allow 4 values to be displayed at once on the x-axis, not more
combinedChart.setVisibleXRangeMaximum(4);
combinedChart.moveViewToX(4);
//animate y axis to 2 sec
combinedChart.animateY(0);
combinedChart.invalidate();
}
//if FlOW RATE button is off
else {
//change text color of flow rate switch
flow_rate_switch_txt.setTextColor(Color.BLACK);
//remove all data sets of area chart when switch turned OFF
boolean indicator = data.removeDataSet(lineData1.getDataSetByLabel("Flow Rate", true));
//notify to combine chart that data set has removed
combinedChart.notifyDataSetChanged();
//clear relative chart
combinedChart.clear();
combinedChart.setNoDataText("No data");
combinedChart.invalidate();
combinedChart.animateX(500);
}
}
});
And my generateLineData() function, which is called when switch turned on to generate chart data
private LineData generateLineData(List<String> arr) {
LineData d = new LineData();
ArrayList<Entry> entries = new ArrayList<Entry>();
for (int index = 0; index < arr.size(); index++)
entries.add(new Entry(Float.parseFloat(arr.get(index)), index));
LineDataSet set = new LineDataSet(entries, "Flow Rate");
//set highlight line color
set.setColor(Color.parseColor("#536BB5"));
set.setLineWidth(2.5f);
//set data points color
set.setCircleColor(Color.RED);
//set color to area occupied by chart
set.setFillColor(Color.parseColor("#536BB5"));
set.setCircleRadius(4f);
//draw chart lines in cubic shape
set.setDrawCubic(true);
//set area occupied by chart must be filled
set.setDrawFilled(true);
set.setDrawValues(false);
set.setHighLightColor(Color.MAGENTA);
set.setLabel("Flow rate");
set.setAxisDependency(YAxis.AxisDependency.LEFT);
d.addDataSet(set);
lineData1 = d;
return d;
}
You need to tell your chart that dataset is updated and you need to refresh chart for that you need to add following lines:
combinedChart.notifyDataSetChanged();
combinedChart.setData(data);
combinedChart.invalidate();
Keeping switch state to turn ON, I got my chart on real time.
Add this code at the beginning of your display chart method
chart.refreshDrawableState();
When values get updated then you you have to set values to the mpChart.
and for updating mpchat just hide and visible the mpChart.
I'm using the MPAndroidChart to create a bar chart. My configurations:
<string-array name="months_initials">
<item>J</item>
<item>F</item>
<item>M</item>
<item>A</item>
<item>M</item>
<item>J</item>
<item>J</item>
<item>A</item>
<item>S</item>
<item>O</item>
<item>N</item>
<item>D</item>
</string-array>
...
String[] months = getResources().getStringArray(R.array.months_initials);
chart.getXAxis().setCenterAxisLabels(true);
chart.getXAxis().setLabelCount(months.length, true);
chart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(months) {
#Override
public String getFormattedValue(float value, AxisBase axis) {
Timber.i("index = %s", value);
return super.getFormattedValue(value, axis);
}
});
index = 0.5
index = 1.5909091
index = 2.6818182
index = 3.7727275
index = 4.8636365
index = 5.9545455
index = 7.0454545
index = 8.136364
index = 9.227273
index = 10.318182
index = 11.409091
And this is the result:
Now, if I change it to: return super.getFormattedValue(value-0.5f, axis);
The result is:
Again, if I add another change: chart.getXAxis().setLabelCount(Integer.MAX_VALUE, true);
index = 0.5
index = 1.0
index = 1.5
index = 2.0
index = 2.5
index = 3.0
index = 3.5
index = 4.0
index = 4.5
index = 5.0
index = 5.5
index = 6.0
index = 6.5
index = 7.0
index = 7.5
index = 8.0
index = 8.5
index = 9.0
index = 9.5
index = 10.0
index = 10.5
index = 11.0
index = 11.5
index = 12.0
index = 12.5
The result is:
A bit "hammer time" but it would work for me, unfortunately the labels are not correctly centered.
So, what's happening here, what am I missing? How can I achieve my final result?
Thanks for your time.
ps: opened an issue too.
EDIT full setup code:
String[] months = getResources().getStringArray(R.array.months_initials);
BarChart chart = binding.barChart;
chart.setTouchEnabled(false);
chart.getDescription().setEnabled(false);
chart.getLegend().setEnabled(false);
chart.getAxisLeft().setEnabled(false);
chart.getAxisRight().setEnabled(false);
chart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
chart.getXAxis().setDrawAxisLine(false);
chart.getXAxis().setDrawGridLines(false);
chart.getXAxis().setCenterAxisLabels(true);
chart.getXAxis().setLabelCount(Integer.MAX_VALUE, true);
chart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(months) {
#Override
public String getFormattedValue(float value, AxisBase axis) {
Timber.i("index = %s", value);
return super.getFormattedValue(value - 0.5f, axis);
}
});
chart.getXAxis().setTextColor(ContextCompat.getColor(this, R.color.grey));
chart.getXAxis().setTextSize(12);
BarDataSet barData = new BarDataSet(data, "data");
barData.setColor(ContextCompat.getColor(this, R.color.chart_bar));
barData.setDrawValues(false);
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(barData);
binding.barChart.setData(new BarData(dataSets));
binding.barChart.animateY(1000, Easing.EasingOption.Linear);
I can't exactly pin point the issue in your code but it is probably something do with the granularity of the chart.
But here's a rough working example. Hope this helps!
String[] months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun"};
BarChart mChart = (BarChart) findViewById(R.id.barChart);
mChart.setDrawBarShadow(false);
mChart.setDrawValueAboveBar(false);
mChart.getDescription().setEnabled(false);
mChart.setDrawGridBackground(false);
XAxis xaxis = mChart.getXAxis();
xaxis.setDrawGridLines(false);
xaxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xaxis.setGranularity(1f);
xaxis.setDrawLabels(true);
xaxis.setDrawAxisLine(false);
xaxis.setValueFormatter(new IndexAxisValueFormatter(months));
YAxis yAxisLeft = mChart.getAxisLeft();
yAxisLeft.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);
yAxisLeft.setDrawGridLines(false);
yAxisLeft.setDrawAxisLine(false);
yAxisLeft.setEnabled(false);
mChart.getAxisRight().setEnabled(false);
Legend legend = mChart.getLegend();
legend.setEnabled(false);
ArrayList<BarEntry> valueSet1 = new ArrayList<BarEntry>();
for (int i = 0; i < 6; ++i) {
BarEntry entry = new BarEntry(i, (i+1)*10);
valueSet1.add(entry);
}
List<IBarDataSet> dataSets = new ArrayList<>();
BarDataSet barDataSet = new BarDataSet(valueSet1, " ");
barDataSet.setColor(Color.CYAN);
barDataSet.setDrawValues(false);
dataSets.add(barDataSet);
BarData data = new BarData(dataSets);
mChart.setData(data);
mChart.invalidate();
RESULT
I had same issue and resolved by doing this :
barChart.getXAxis().setLabelCount(barDataSet.getEntryCount());
Moreover if you are using only one dataset then instead of this:
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(barData);
Use:
BarDataSet barDataSet = new BarDataSet(yourValauesHere, "Legend Text here");
For reference please follow example below:
barEntries = new ArrayList<BarEntry>();
barEntries.add(new BarEntry(0, 1));
barEntries.add(new BarEntry(1, 2));
barEntries.add(new BarEntry(2, 4));
barEntries.add(new BarEntry(3, 6));
barEntries.add(new BarEntry(4, 5));
barEntries.add(new BarEntry(5, 7));
barDataSet = new BarDataSet(barEntries, "Contracts");
barDataSet.setAxisDependency(YAxis.AxisDependency.LEFT);
barDataSet.setColor(getColor("defaultYellow"));
barDataSet.setHighlightEnabled(true);
barDataSet.setHighLightColor(Color.RED);
barDataSet.setValueTextSize(defaultValueTextSize);
barDataSet.setValueTextColor(getColor("primaryDark"));
BarData barData = new BarData(barDataSet);
barChart.getDescription().setText("No. of Contracts signed in 6
months");
barChart.getDescription().setTextSize(12);
barChart.getAxisLeft().setAxisMinimum(0);
barChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTH_SIDED);
barChart.getXAxis().setValueFormatter(new
IndexAxisValueFormatter(getXAxisValues()));
barChart.animateY(1000);
barChart.setData(barData);
Getting xAxis values method:
private ArrayList<String> getXAxisValues()
{
ArrayList<String> labels = new ArrayList<String> ();
labels.add( "JAN");
labels.add( "FEB");
labels.add( "MAR");
labels.add( "APR");
labels.add( "MAY");
labels.add( "JUN");
return labels;
}
Handling axis labels like this can be rather complicated sometimes. You have to first make sure the labels are shown at the interval and spacing you want, then you have to use an axis value formatter to transform them to some other string in this case.
The TL;DR is
setCenterAxisLabels does not do what it sounds like it does - don't use it. Labels are already centered under their axis values by default. This centers the labels between axis values.
Use numeric labels to debug label position before applying IndexAxisValueFormatter to make sure you have integer values in the expected locations.
IndexAxisValueFormatter has some buggy rounding logic and doesn't display labels for values between X.5 and X.999999, while values between X.0 and X.499999 get rounded down to X and labels[X] is shown. Use a custom ValueFormatter to fix those issues.
Long Answer
To start with, I used the following test data, which reproduces the behavior you saw (the behavior will be a bit different if your x values start at 0 instead of 1, but the same type of issues will show up). The code here is in Kotlin, but the solution is equally applicable to Java.
val months = listOf("J","F","M","A","M","J","J","A","S","O","N","D")
val yvals = listOf(1f,2f,5f,0.1f,0.5f,1f,0.5f,2f,1f,2.5f,2f,0.2f)
val entries = yvals.mapIndexed { i, y -> BarEntry((i+1).toFloat(),y)}
val barDataSet = BarDataSet(entries,"data")
Initial Approach
When you plot this using your initial chart settings
val xAxis = barChart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.setDrawGridLines(true)
xAxis.setDrawAxisLine(true)
xAxis.setCenterAxisLabels(true)
xAxis.setLabelCount(months.size, true)
xAxis.valueFormatter = IndexAxisValueFormatter(months)
you get the first behavior you saw where half the labels are gone and the rest are not positioned under the bars:
To better understand why this is happening, we can use a different axis value formatter to see where MPAndroidChart is trying to place labels. This will write the numeric values instead of trying to convert them to array indices:
class VerboseAxisValueFormatter : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return "%.2f".format(value)
}
}
This gives (the values match the ones you saw when debugging)
Looking at the source code for IndexAxisValueFormatter
public String getFormattedValue(float value, AxisBase axis) {
int index = Math.round(value);
if (index < 0 || index >= mValueCount || index != (int)value)
return "";
return mValues[index];
}
the final check in there (index != (int)value) means that if the rounded value is not equal to the truncated (floor) value then the result is blank. This means that for values from 1.0 to 1.499999 it will return mValues[1] but for values from 1.5 to 1.99999 it will return a blank string. That's why the entire first half of the chart was missing labels - the x values it chose to draw axis labels at had decimal portions greater than 0.5.
Second Approach
So for your second iteration, you set the label count to Integer.MAX_VALUE and shifted the values down by 0.5. Doing that here (but leaving the numeric labels on) gives the following, where the labels have nice even spacing (default granularity here is 0.5). If you replaced the integer values here (0.0, 1.0, etc) you would get the result you saw with your "offset label case" (labels were present but not centered)
However, the left bar is centered at 1.0 which is very different from where the label was drawn. This is because xAxis.setCenterAxisLabels(true) does not center labels under their values (which would make sense, but that's actually the default behavior already) but instead centers labels between the actual axis values. Turning this off and removing the 0.5f shift, gives
Now, we finally have integer value labels centered below bars and in the correct positions, which means we can use the IndexAxisValueFormatter - however, the array indices are 0-based and the data set is 1-based. To fix that, you can subtract 1 from the x values either in the data set or in the formatter. Even so, it's not quite right - we get an extra label on the left edge because the -0.5 there rounds and truncates to 0.
Working Solution
In the end, once we have set the label position correctly, the best choice is still to write a custom ValueFormatter that doesn't have all the issues in the built-in IndexAxisValueFormatter. Instead of using index != (int)value we use abs(index - x) > 0.01f so that the behavior is symmetric and only shows labels actually at integer positions.
class FixedIndexAxisValueFormatter(private val offset: Float, private val labels: List<String>) : ValueFormatter()
{
override fun getFormattedValue(value: Float): String {
val x = value - offset
val index = x.roundToInt()
return if( index < 0 || index >= labels.size || abs(index - x) > 0.01f) ""
else labels[index]
}
}
Using this formatter finally gives the correct result
The final settings to get this were
// Force it to draw labels at all positions controlled by the axis
// bounds and granularity
xAxis.setLabelCount(Integer.MAX_VALUE, true)
// These are already the defaults in this case so they're not necessary,
// but good to specify explicitly in case the defaults change later
xAxis.axisMinimum = 0.5f
xAxis.axisMaximum = 12.5f
xAxis.granularity = 0.5f
// Use a better value formatter, with an offset of 1 since the
// data set started at 1
xAxis.valueFormatter = FixedIndexAxisValueFormatter(1f, months)
YAxis leftAxis1 = barChart.getAxisLeft();
leftAxis1.setDrawLabels(true);
leftAxis1.setDrawAxisLine(false);
leftAxis1.setDrawGridLines(true);
leftAxis1.setEnabled(true);
leftAxis1.setDrawZeroLine(true);
//leftAxis1.setTypeface(); // set a different font
leftAxis1.setTextSize(12f); // set the text size
leftAxis1.setAxisMinimum(0f); // start at zero
leftAxis1.setAxisMaximum(100f); // the axis maximum is 100
leftAxis1.setTextColor(Color.BLACK);
leftAxis1.setValueFormatter(new MyValueFormatter());
leftAxis1.setGranularity(1f); // interval 1
leftAxis1.setLabelCount(9, true);
//leftAxis1.setDrawZeroLine(true);
YAxis rightAxis1 = barChart.getAxisRight();
rightAxis1.setEnabled(false);
}
private class MyValueFormatter implements IAxisValueFormatter {
private DecimalFormat mFormat;
public MyValueFormatter() {
mFormat = new DecimalFormat("###,###,##0.0"); // use one decimal
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
return mFormat.format(value) + " $"; // e.g. append a dollar-sign
}
}
}
I want to have my graph's y-axis values similar to the ones shown in the above image. How would I make it like that? There should be intervals of 10 between for the y-values. Is there any way to do this?
This should help you:
mChart.getAxisLeft().setGranularity(10);
It will make Y axis values increase by 10 and you will have desired result. Howerever, if you zoomin, the values and graph will be changed (it is normal). If you also want to disable zooming in and out you can disable it :
mChart.setScaleEnabled(false);
Hope that helps
Set
rightAxis1.setEnabled(false);
to
rightAxis1.setEnabled(true);
I am using MPChart for displaying a Barchart. My values are positive and negative. I want to show xAxis as used in Math of traditional way. Using this code my chart display vertical lines (only zero wanted)
mXAxis = mChart.getXAxis();
mXAxis.setDrawGridLines(false);
mXAxis.setEnabled(false);
mYAxis = mChart.getAxisLeft();
mYAxis.setDrawAxisLine(false);
mYAxis.setDrawGridLines(true);
mYAxis.setStartAtZero(false);
mYAxisRight = mChart.getAxisRight();
mYAxisRight.setEnabled(false);
mYAxisRight.setDrawGridLines(false);
Please, provide some sample for removing all horizontal lines but zero
EDIT:
Even when yAxis.setLabelCount(1) (1 because zero value needed to shown) the impl looks like:
public void setLabelCount(int yCount) {
if(yCount > 25) {
yCount = 25;
}
if(yCount < 2) {
yCount = 2;
}
this.mLabelCount = yCount;
}
So, is it recommended to override this implementation?
I have got solution with labels at bottom
Use this in your code
mXAxis = mChart.getXAxis();
mXAxis.setDrawGridLines(false);
mXAxis.setEnabled(true);
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);