Hi!
I'm using the library to dynamicaly display new data in a line chart. I implemented the adding of values based on the RealTimeLineChart example, but I'm worrying that I also need to remove old data to avoid out of memory error or something similar. So, for that reason I added remove entry code, but the graph behaves very strange. Here is my code for adding entires:
LineDataSet set = data.getDataSetByIndex(0);
if (set == null) {
set = createSet();
data.addDataSet(set);
}
// Add new value
data.addXValue("");
data.addEntry(new Entry((float) value, set.getEntryCount()), 0);
data.setDrawValues(false);
// Remove oldest value
if (data.getXValCount() > VISIBLE_VALUE_RANGE) {
data.removeEntry(0, 0);
data.removeXValue(0);
}
// Let the chart know it's data has changed
lineChartAccelerometer.notifyDataSetChanged();
// Limit the number of visible entries
lineChartAccelerometer.setVisibleXRange(VISIBLE_VALUE_RANGE);
// Move to the latest entry
lineChartAccelerometer.moveViewToX(data.getXValCount() - (VISIBLE_VALUE_RANGE + 1));
The VISIBLE_VALUE_RANGE is 100, and when the value count reaches that, graph starts to disappear from the beginning to the end, and no new value appears. I logcated the entry count, and it seems like everything is fine, after count reaches 100, it stays there, so one value is added and one is remvode.
What am I doing wrong?
"the graph behaves strange" - what does that mean?
What exactly do you want?
Display 100 values maximum, then remove from the start and add to the end?
Yes I want just that. Values keeps coming in, and I want to display the last 100. To avoid filling in the memory, I figured I have to remove the older data.
The graph does what I said at the end of my previous comment: "The VISIBLE_VALUE_RANGE is 100, and when the value count reaches that, graph starts to disappear from the beginning to the end, and no new value appears."
Since you are removing values upon overflow, the methods setVisibleXRange(...) and moveViewToX(...) do not really make sense in your case.
Try removing those methods and call invalidate() on the chart upon each new entry added (after calling notifyDataSetChanged().
I tried to do what you suggested. It does the same: the graph starts to disappear from the beginning.
Maybe it helps if I'm further clarify what I'm trying to achieve.
I have a bunch of sensor data coming in. A double value comes in every 50 milliseconds. I have to display it on a graph, quite similarly to an ECG. And it needs to be able to run for several hours.
I'm trying to do something sort of similar i.e. removing from the front and adding new items to the back. Looking at the code for removing xValues I see that it only removes the entry from the ArrayList but does not perform an update of the entries in the datasets that now might have an invalid xValue.
Dynamical adding of data seems to work fine but removal produces a ton of problems. Specifically I get ArrayOutOfBounds exceptions in the draw step for some reasons that I can't quite pin down yet.
java.lang.ArrayIndexOutOfBoundsException: length=4; index=4
at com.github.mikephil.charting.buffer.LineBuffer.lineTo(LineBuffer.java:36)
at com.github.mikephil.charting.buffer.LineBuffer.feed(LineBuffer.java:53)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawLinear(LineChartRenderer.java:286)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawDataSet(LineChartRenderer.java:115)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawData(LineChartRenderer.java:88)
at com.github.mikephil.charting.charts.BarLineChartBase.onDraw(BarLineChartBase.java:202)
going all the way up to Choreographer. So this looks like something internally is breaking when removing values both from datasets and xvalues.
Iterating through the entries & decreasing their xValues seem to solve most problems:
for (Entry entry : set.getYVals()) {
entry.setXIndex(entry.getXIndex() - 1);
}
As for the drawing exceptions, I had to add a bunch of
List<Entry> entries = dataSet.getYVals();
if (entries.size() < 1) {
return;
}
in LineChartRenderer
My experiments are still on-going, some things are still weird (e.g. setVisibleXRange won't work after removing a value)
@Blackclaws I had a similar problem, exceptions popped up randomly for things related to buffer. For instance:
java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
at com.github.mikephil.charting.buffer.CircleBuffer.addCircle(CircleBuffer.java:15)
at com.github.mikephil.charting.buffer.CircleBuffer.feed(CircleBuffer.java:27)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawCircles(LineChartRenderer.java:511)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawExtras(LineChartRenderer.java:471)
at com.github.mikephil.charting.charts.BarLineChartBase.onDraw(BarLineChartBase.java:236)
java.lang.ArrayIndexOutOfBoundsException: length=40; index=40
at com.github.mikephil.charting.renderer.LineChartRenderer.drawCircles(LineChartRenderer.java:519)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawExtras(LineChartRenderer.java:471)
at com.github.mikephil.charting.charts.BarLineChartBase.onDraw(BarLineChartBase.java:236)
at android.view.View.draw(View.java:15312)
java.lang.ArrayIndexOutOfBoundsException: length=56; index=56
at com.github.mikephil.charting.buffer.LineBuffer.lineTo(LineBuffer.java:38)
at com.github.mikephil.charting.buffer.LineBuffer.feed(LineBuffer.java:54)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawLinear(LineChartRenderer.java:296)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawDataSet(LineChartRenderer.java:114)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawData(LineChartRenderer.java:86)
at com.github.mikephil.charting.charts.BarLineChartBase.onDraw(BarLineChartBase.java:218)
java.lang.ArrayIndexOutOfBoundsException: length=12; index=12
at com.github.mikephil.charting.buffer.LineBuffer.lineTo(LineBuffer.java:36)
at com.github.mikephil.charting.buffer.LineBuffer.feed(LineBuffer.java:52)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawLinear(LineChartRenderer.java:296)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawDataSet(LineChartRenderer.java:114)
at com.github.mikephil.charting.renderer.LineChartRenderer.drawData(LineChartRenderer.java:86)
at com.github.mikephil.charting.charts.BarLineChartBase.onDraw(BarLineChartBase.java:218)
...
As I remember, the exceptions popped up only when I was touching the chart. These are AIOOB exceptions, which means the buffers weren't large enough. They're created at ~ line 57 of LineChartRenderer, and their sizes are correlated to the number of datasets & to the number of entries in the datasets. If such exceptions happen, that's because the number of datasets or the number of entries in the datasets has increased between the time the buffers were created & the time the chart was rendered (so that's probably not because of dynamic removal!).
Now, what triggers buffer creation? A call to notifyDataSetChanged on LineChart. The drawing methods are called whenever the chart is invalidated (and I guess touching the chart triggers invalidate).
Therefore, the exceptions happen for one of these reasons: you either forgot to call notifyDataSetChanged after adding a new entry to one of your datasets, or you're calling it too late, after the chart was invalidated (maybe because of a multi-threaded application)!
If somehow you can't fix the call to notifyDataSetChanged, you could reinitialize the buffer before each redraw. In BarLineChartBase, at the beginning of onDraw method, you could add "mRenderer.initBuffers();". It's a nasty fix, and it's probably bad for performance. But it seems to have fixed my issues.
Now, this is just a guess on what might cause the issue. I'm not 100% sure it has actually fixed anything, and my application will probably crash right after posting this message just to prove me wrong. But until then, I'm sticking with this fix!
I also had this problem.I want to show the last 5 entries in a LineChart,so when a data is comming,I call addXValue() and addEntry().Since the entry count is 5,I call removeXValue(0) and removeEntry(0) to remove the oldest entry,but the LineChart is very strange:in fact the entry count is 5,but it only one entry in the end,now I don't know how to do with it.Here is my code:
private static final int VISIBLE_NUM = 5;
private void refreshData(float value) {
LineData data = mChart.getData();
if (data != null) {
LineDataSet set = data.getDataSetByIndex(0);
if (set == null) {
set = new LineDataSet(null, "DataSet");
set.enableDashedLine(10f, 5f, 0f);
set.setColor(Color.BLUE);
set.setCircleColor(Color.GREEN);
set.setLineWidth(1f);
set.setCircleSize(3f);
set.setDrawCircleHole(false);
set.setValueTextSize(9f);
set.setFillAlpha(65);
set.setFillColor(Color.BLACK);
data.addDataSet(set);
}
if(set.getEntryCount() == VISIBLE_NUM) {
data.removeXValue(0);
set.removeEntry(0);
}
data.addXValue(new SimpleDateFormat("HH:mm:ss")
.format(new Date(System.currentTimeMillis())));
data.addEntry(new Entry(value, set.getEntryCount()), 0);
mChart.notifyDataSetChanged();
//mChart.setVisibleXRange(VISIBLE_NUM-1);
//mChart.moveViewToX(data.getXValCount() - VISIBLE_NUM);
mChart.invalidate();
}
}
Try this:
private static final int VISIBLE_NUM = 5;
private void refreshData(float value) {
LineData data = mChart.getData();
if (data != null) {
LineDataSet set = data.getDataSetByIndex(0);
if (set == null) {
set = new LineDataSet(null, "DataSet");
set.enableDashedLine(10f, 5f, 0f);
set.setColor(Color.BLUE);
set.setCircleColor(Color.GREEN);
set.setLineWidth(1f);
set.setCircleSize(3f);
set.setDrawCircleHole(false);
set.setValueTextSize(9f);
set.setFillAlpha(65);
set.setFillColor(Color.BLACK);
data.addDataSet(set);
}
if(set.getEntryCount() == VISIBLE_NUM) {
data.removeXValue(0);
set.removeEntry(0);
for (Entry entry : set.getYVals()) {
entry.setXIndex(entry.getXIndex() - 1);
}
}
data.addXValue(new SimpleDateFormat("HH:mm:ss")
.format(new Date(System.currentTimeMillis())));
data.addEntry(new Entry(value, set.getEntryCount()), 0);
mChart.notifyDataSetChanged();
//mChart.setVisibleXRange(VISIBLE_NUM-1);
//mChart.moveViewToX(data.getXValCount() - VISIBLE_NUM);
mChart.invalidate();
}
}
Yes, this is useful,thanks!
Thank you for sharing your solution. I miss this workaround from the wiki page.
I know the issue is closed, but Google easily gives it in search results.
I would like to share latest code variant for v3.0.0-beta1, because I've spent time fixing and debugging new code base.
private static final int VISIBLE_NUM = 5;
private void refreshData(float value) {
LineData data = mChart.getData();
if (data != null) {
// at least one entry exists
LineDataSet set = data.getDataSetByIndex(0); // should not be null, because we created it before
set.setColor(Color.BLUE);
set.setDrawCircleHole(false);
set.setValueTextSize(9f);
data.addDataSet(set);
// check and remove oldest entry
if(set.getEntryCount() == VISIBLE_NUM) {
set.removeEntry(0); // remove oldest
// change Indexes - move to beginning by 1
for (Entry entry : set.getValues()) {
entry.setX(entry.getX() - 1);
}
}
// add Entry to the end of Line with X position = getEntryCount(), Y pos as = value
data.addEntry(new Entry(set.getEntryCount(), value), 0);
} else {
// create all necessary stuff on the first run
ArrayList<ILineDataSet> dataSets = new ArrayList<>(); // create
data = new LineData(dataSets); // create
LineDataSet mySet = new LineDataSet(null, getString(R.string.my_graph_name)); // create empty
mySet.setColor(Color.BLUE);
// ..... the rest of settings
data.addDataSet(mySet); // should be assigned to parent BEFORE adding new Entry
data.addEntry(new Entry(mySet.getEntryCount(), value), 0); // add FIRST Entry to single mySet with index=0
mChart.setData(data);
}
// notify about changes
mChart.notifyDataSetChanged();
mChart.invalidate();
}
Thank you everyone for writing about this issue - I was also repro'ing the AIOOB exception for dynamically removing data for v2.2.2. Going to try upgrading to the 3.0.1beta and see if I still get it.
I would like to share the latest solution for v3.0.2
if (set.getEntryCount() >= X_COUNT_MAX) {
set.removeFirst();
for (int i=0; i<set.getEntryCount(); i++) {
Entry entryToChange = set.getEntryForIndex(i);
entryToChange.setX(entryToChange.getX() - 1);
}
}
@hzw1199 works like a charme
void upDateGraph() {
xVals.add(count+"");//strings for xvalues
count++;
yVals.add(new Entry(random.nextInt(180- 60)+20, count));//yVals is list of Entry object
if(yVals.size()>20) { // if size is more than 20 than remove element
mChart.getLineData().getDataSetByIndex(0).removeEntry(0);
}
mChart.notifyDataSetChanged();
mChart.invalidate();
}
Most helpful comment
Try this: