MPAndroidChart - wrong values in getFormattedValue method. Where they come from? - android

I am working with a project which is using MPAndroidChart library which makes me really crazy, I want to remove it.
The problem is I have created a custom ValueFormatter and I can't understand where these values come from, all are wrong.
private void setData() {
for (int i = 1; i <= 10; i++) {
Entry entry = new Entry(i, i);
values.add(entry);
}
IAxisValueFormatter valueFormatter = new myValueFormatter();
XAxis xAxis = mChart.getXAxis();
xAxis.setValueFormatter(valueFormatter);
LineDataSet set1 = new LineDataSet(values, "DataSet 1");
ArrayList<ILineDataSet> dataSets = new ArrayList<ILineDataSet>();
dataSets.add(set1); // add the datasets
// create a data object with the datasets
LineData data = new LineData(dataSets);
// set data
mChart.setData(data);
}
custom formatter class:
I have an array which has 1,2,3,4,5,6,7,8,9,10 values
but I get 2,4,6,8,10 values in getFormattedValue method.
public classmyValueFormatter implements IAxisValueFormatter {
#Override
public String getFormattedValue(float value, AxisBase axis) {
System.out.println(value); //Here I get odd values where they come from I don't know.
}
}

Well, generally that's how library is written. Have a look here:
https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java#L205
String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis);
Author's intention was probably to give more spacing between labels. If you think this is a bug, submit issue to library's repository on Github.

Related

Drawing horizontal bar chart with `MPAndroidChart`

Does anybody have an idea on how to make this: my app image look like this: how it should be?
My question is: how to make each line of the Horizontal Bar Chart fill between the two lines?
for (Iterator<Map.Entry<String, Float>> iterator = structuredData.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<String, Float> entry = iterator.next();
//i*10 => starting position of the bar
horizontalBarChartArr.add(new BarEntry((i*10), entry.getValue()));
names.add(entry.getKey());
i++;
}
XAxis xAxis = horizontalBarChart.getXAxis();
xAxis.setValueFormatter(new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return names.get((int)value % names.size());
}
});
xAxis.setDrawGridLines(true);
xAxis.setDrawAxisLine(true);
BarDataSet barDataSet = new BarDataSet(horizontalBarChartArr, "Day Expenses");
barDataSet.setColors(Color.RED);
BarData data = new BarData(barDataSet);
data.setBarWidth(9f);
horizontalBarChart.setDoubleTapToZoomEnabled(false);
Description a = new Description();
a.setText("");
horizontalBarChart.setDescription(a);
horizontalBarChart.invalidate();
horizontalBarChart.animateY(1200);
horizontalBarChart.setData(data);
Check this example to your case:
https://github.com/ddanny/achartengine
It´s Automatically exported from code.google.com/p/achartengine

ValueFormatter throws IndexOutOfBoundsException

I have a small problem. I'm trying to make a listView of BarCharts, where xAxis represents categories, and yAxis - cost.
Here is my data.
entry
entries.add(new BarEntry(xAxis, cost, categoryName));
xAxis is just a position in chart, I put category name as 3rd parameter. Then I put it in array list of BarData
ArrayList<BarData> months = new ArrayList<>();
BarDataSet set = new BarDataSet(entries, monthName);
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(set);
BarData barData = new BarData(dataSets);
months.add(barData);
Then in adapter I set value formatter
BarData data = getItem(position);
...
xAxis.setValueFormatter(new MyValueFormatter(data.getDataSetByIndex(0)));
and in MyValueFormatter I'm trying to get category name from entry and set it as xAxis value.
public class MyValueFormatter implements IAxisValueFormatter {
private IBarDataSet mDataSet;
public StatisticByMonthValueFormatter(IBarDataSet data) {
mDataSet = data;
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
return (String) mDataSet.getEntryForIndex((int) value).getData();
}
}
This woks, but sometimes when I'm scrolling list view I get
java.lang.IndexOutOfBoundsException: Invalid index 4, size is 2.
I know, that bug in the getFormattedValue method in MyValueFormatter, but i don't understand how can I fix this, or how implement this in the right way?
DataSet#getEntryForIndex(int index)
is the wrong method to call here. Entry in a DataSet are stored in a backing array and this method will get the Entry at that index in the backing array. This doesn't always correspond to the Entry for that x-value (the x-value on the chart). Hence, the IndexOutOfBoundsException.
You would want to do something this instead:
return (String) mDataSet.getEntryXPos(float value).getData();
See the javadoc for DataSet for further explanation

I am trying to make values of X axis show on the bottom of the graph not the top, using MP android chart

I created a graph app using MP android chart, i used it to plot two different line data sets . But my problem is that the value labels for the xaxis appear on the top of the graph . I need a way to make it to the bottom . below is my code
final LineChart linechart = (LineChart)findViewById(R.id.idlinechart);
//created list enties to hold the values
List<Entry> valuesCompany1 = new ArrayList<>(); List<Entry> valuesCompany2 = new ArrayList<>();
//created entry values to be entered into the lsit entry b
Entry c1e1 = new Entry(0f, 100000f); valuesCompany1.add(c1e1);
Entry c1e2 = new Entry (1f, 140000f);valuesCompany1.add(c1e2);
Entry c1e3 = new Entry(2f,90000f);valuesCompany1.add(c1e3);
Entry c1e4 = new Entry (3f, 150000f); valuesCompany1.add(c1e4);
Entry c2e1 = new Entry(0f, 50000f); valuesCompany2.add(c2e1);
Entry c2e2 = new Entry (1f, 50000f);valuesCompany2.add(c2e2);
Entry c2e3 = new Entry(2f,150000f);valuesCompany2.add(c2e3);
Entry c2e4 = new Entry (3f, 110000f); valuesCompany2.add(c2e4);
//so now we create line data to hold the list
LineDataSet linedatasetComp1 = new LineDataSet(valuesCompany1, "company 1");
linedatasetComp1.setAxisDependency(YAxis.AxisDependency.LEFT);
linedatasetComp1.setColor(Color.BLUE);
LineDataSet linedatasetComp2 = new LineDataSet(valuesCompany2, "company 2");
linedatasetComp2.setAxisDependency(YAxis.AxisDependency.LEFT);
linedatasetComp2.setColor(Color.RED);
List<ILineDataSet> iLinedata = new ArrayList<ILineDataSet>();
iLinedata.add(linedatasetComp1); //adding the line data sets into the line data list
iLinedata.add(linedatasetComp2);
//setting the Ilinedata list into line data
LineData linedata = new LineData(iLinedata);
//setting the line data into line chart
linechart.setData(linedata);
linechart.invalidate(); //refreshing the line chart
//Saving the picture to galery
Button savebutton = (Button)findViewById(R.id.button);
savebutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
linechart.saveToGallery("new Line chart", 150);
Toast.makeText(getApplicationContext(), "graph saved ", Toast.LENGTH_LONG).show();
}
});
final String[] Quaters = {"Q1", "Q2", "Q3", "Q4"};
IAxisValueFormatter valueFormater = new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return Quaters[(int) value];
}
#Override
public int getDecimalDigits() {
return 0;
}
};
XAxis xAxis = linechart.getXAxis();
xAxis.setGranularity(1f);
xAxis.setValueFormatter(valueFormater);
Description desc = new Description();
desc.setText("A graph comparing revenuews of two companies ");
desc.setEnabled(true);
desc.setPosition(0,0);
desc.setTextColor(Color.BLACK);
linechart.setDescription(desc);
I don't know if LineChart has that option, but in case it does you just have to get the XAxis adjust the Position like this:
linechart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
Hope it helps!
Use MPAndroid chart version 2.2.4 or above and use the code
mChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
as said by Ponchotg

MPAndroidChart adding and display bar chart label

Just started using the MPAndroidChart version 3.0.0 beta and i have created a project that can show my values in a bar chart. My question is where do i add and display labels.
Each Bar should have its own label eg. "Flue", "Cheese" etc at the bottom. Not sure what function does this, am actively searching and reading the Docs/Wiki but no joy currently.
Depending on your preferences, you can use the data property of an Entry to store the label and then return it in your IAxisValueFormatter implementation:
public class LabelValueFormatter implements IAxisValueFormatter {
private final DataSet mData;
public LabelValueFormatter(DataSet data) {
mData = data;
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
// return the entry's data which represents the label
return (String) mData.getEntryForXPos(value, DataSet.Rounding.CLOSEST).getData();
}
}
This approach allows you to use the Entry constructor (or BarEntry in this case) to add the labels, which can improve the readability of your code:
ArrayList<BarEntry> entries = new ArrayList<>();
for (int i = 0; i < length; i++) {
// retrieve x-value, y-value and label
entries.add(new BarEntry(x, y, label));
}
BarDataSet dataSet = new BarDataSet(entries, "description");
BarData data = new BarData(dataSet);
mBarChart.setData(data);
mBarChart.getXAxis().setValueFormatter(new LabelValueFormatter(data));
Also check out this answer for more information and an alternative approach on using labels with the BarChart and the new 3.0.0 version of the library.

MPAndroidChart Legend with different shapes (forms)

I am using MPAndroidChart and have the need to plot a number of datasets using ScatterChart. As the number of datasets is dynamic, I used a logic that creates a combination of color and shape for each dataset. The resultant chart looks like this. As you can see, in this example, there are ten datasets, each represented by a unique Shape+Color combination. The problem is with the legend shapes. How do I change the legend shapes to match with the dataset shapes?
Chart:
Source Code:
private static final int SCATTER_SHAPES_MOD = 4; //there are four shapes
private static final int SCATTER_COLOR_MOD = 5; //there are five colors
for (int i=0; i < dataSeries.size(); i++) {
ScatterDataSet set = new ScatterDataSet(dataSeries.get(i), choiceArray[i]);
set.setScatterShape(ScatterShapeArray[i % SCATTER_SHAPES_MOD]);
set.setColor(ColorTemplate.COLORFUL_COLORS[i % SCATTER_COLOR_MOD]);
set.setScatterShapeSize(10f);
set.setDrawValues(false); // Hide data labels
dataSets.add(set); // add the dataset
}
if (dataSeries.size() > 0) {
data = new ScatterData(xVals, dataSets);
// Set chart data
chart.setData(data);
}
Actually, now Dear Philipp has added that functionality, I think he forgot to update here, hence I am updating so that for someone it may help.
There is a new function called setCustom in legend class which can be used for the above purpose.I have tested it.Please find code below. The looping is done with dataSets I had already(ScatteredDataSet in my case).
List<LegendEntry> entries = new ArrayList<>();
for (int i = 0; i < dataSets.size(); i++) {
final LegendEntry entry = new LegendEntry();
IScatterDataSet set = dataSets.get(i);
String Label = set.getLabel();
if (Label.equalsIgnoreCase("A")) {
entry.form = Legend.LegendForm.CIRCLE;
entry.formColor = set.getColor();
entry.label = Label;
} else if(Label.equalsIgnoreCase("B")){
entry.form = Legend.LegendForm.CIRCLE;
entry.formColor = set.getColor();
entry.label = Label;
} else if(Label.equals("C")){
entry.form = Legend.LegendForm.SQUARE;
entry.formColor = set.getColor();
entry.label = Label;
}
else if (entry.formColor == ColorTemplate.COLOR_NONE ||
entry.formColor == 0) {
entry.form = Legend.LegendForm.EMPTY;
entry.label = Label;
}
entries.add(entry);
}
LegendEntry[] mEntries = entries.toArray(new LegendEntry[entries.size()]);
Legend l = mChart.getLegend();
l.setCustom(mEntries);
l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT);
l.setOrientation(Legend.LegendOrientation.HORIZONTAL);
l.setDrawInside(false);
l.setXOffset(10f);
I Thank Philipp for this awesome library.
NOTE:Only limitation I see is that LegendForm has only few shapes as enum which can't be altered and Scatter chart has more shapes which can't be replicated in Legends!If there is a way please post here.
Unfortunately that is currently not possible by default.
You will have to modify the library to get the behaviour you are describing.
I might add a feature like that in the future.

Categories

Resources