我目前正在使用achartengine来显示使用蓝牙接收的实时数据。 数据在特定线程上正确收集,并在Bundle中100ms发送到我的主要活动。
主要活动包含来自achartengine库(LineChart类)的4个图表,使用GraphicalView添加到其视图
总而言之,我的主要活动每100毫秒收到一次回调和
调用重绘时,数据集将不会更新,直到下一个包从其他线程到达,100毫秒后。我假设100毫秒应该足以重绘视图。
我在这里发布了一个包含数据集和视图的小版本, 以及实施和使用的主要活动的一部分
/** This class contains the data and renderer used to draw a chart */
public class LineChartData
{
public static enum Series {X, Y, Z};
public static final int MAX_NB_VALUES_PER_SERIE = 1000;
private XYMultipleSeriesDataset mDataset = new XYMultipleSeriesDataset();
private XYMultipleSeriesRenderer mRenderer = new XYMultipleSeriesRenderer();
private int[] mColors = new int[]{Color.RED, Color.GREEN, Color.BLUE};
private GraphicalView mChartView;
private boolean mFollow = true;
private boolean mTouchedDown = false;
public LineChartData(Context context, String title, String[] seriesNames)
{
// Since we save only 3 colors, the programm will not support more than 3 series per chart
for(int i = 0; i < seriesNames.length; ++i)
{
XYSeries serie = new XYSeries(seriesNames[i].toString());
mDataset.addSeries(serie);
XYSeriesRenderer serieRenderer = new XYSeriesRenderer();
serieRenderer.setColor(mColors[i]);
mRenderer.addSeriesRenderer(serieRenderer);
}
mRenderer.setMargins(new int[] { 20, 30, 0, 0 });
mRenderer.setLegendHeight(50);
//mRenderer.setShowLegend(true);
mRenderer.setChartTitle(title);
mRenderer.setMarginsColor(Color.WHITE);
mRenderer.setXAxisMax(SettingsActivity.DEFAULT_DISPLAY_RANGE);
mRenderer.setXAxisMin(0);
mChartView = ChartFactory.getLineChartView(context, mDataset, mRenderer);
mChartView.repaint();
}
public void addData(int serieId, double x, double y)
{
mDataset.getSeriesAt(serieId).add(x, y);
}
public void repaint()
{
mChartView.invalidate();
}
public void updateDisplayWindow(int displayRange)
{
if(!mFollow)
{
return;
}
double maxX = mDataset.getSeries()[0].getMaxX();
if(maxX > displayRange)
{
mRenderer.setXAxisMax(maxX);
mRenderer.setXAxisMin(maxX-displayRange);
}
else
{
mRenderer.setXAxisMax(displayRange);
mRenderer.setXAxisMin(0);
}
}
public void deleteOldestValues()
{
XYSeries[] series = mDataset.getSeries();
for(XYSeries serie : series)
{
while(serie.getItemCount() > MAX_NB_VALUES_PER_SERIE)
{
serie.remove(0);
}
}
}
}
创建4个图表:
protected void onCreate(Bundle b)
{
mAccelChartView = new LineChartData(this, getString(R.string.Accel), new String[]
{getString(R.string.xAxis), getString(R.string.zAxis), getString(R.string.zAxis)});
mGyroChartView = new LineChartData(this, getString(R.string.Gyro), new String[]
{getString(R.string.xAxis), getString(R.string.zAxis), getString(R.string.zAxis)});
mPressureChartView = new LineChartData(this, getString(R.string.Pressure), new String[]
{getString(R.string.pressureAxis)});
mEcgChartView = new LineChartData(this, getString(R.string.ECG), new String[]
{getString(R.string.ecgAxis)});
LinearLayout layoutAccel = (LinearLayout) findViewById(R.id.accelChart);
LinearLayout layoutGyro = (LinearLayout) findViewById(R.id.gyroChart);
LinearLayout layoutPressure = (LinearLayout) findViewById(R.id.pressureChart);
LinearLayout layoutEcg = (LinearLayout) findViewById(R.id.ecgChart);
layoutAccel.addView(mAccelChartView.getView(), 0);
layoutGyro.addView(mGyroChartView.getView(), 0);
layoutPressure.addView(mPressureChartView.getView(), 0);
layoutEcg.addView(mEcgChartView.getView(), 0);
}
更新图表
private void updateViewUsingMessage(String action, Bundle newData)
{
if(action.equals(BluetoothCollecterThread.MSG_UPDATE_CHART))
{
addFromBundle(newData, mLogger.isLogging());
mAccelChartView.updateDisplayWindow(mDisplayRange);
mAccelChartView.deleteOldestValues();
mGyroChartView.updateDisplayWindow(mDisplayRange);
mGyroChartView.deleteOldestValues();
mPressureChartView.updateDisplayWindow(mDisplayRange);
mPressureChartView.deleteOldestValues();
mEcgChartView.updateDisplayWindow(mDisplayRange);
mEcgChartView.deleteOldestValues();
mAccelChartView.repaint();
mGyroChartView.repaint();
mPressureChartView.repaint();
mEcgChartView.repaint();
}
}
它可能看起来很大但实际上很容易。我有两个问题,我不知道是否有联系: 首先,每秒几次,一张图表中的第一个系列在另一张图表上绘制(见下图)。我检查了我的代码,找不到任何解决方案。而且,它似乎真的是随机发生的,所以它看起来更像是来自achartengine库的并发问题。
这带来了我的第二个问题。日志显示来自垃圾收集器的消息过多,特别是WAIT_FOR_CONCURRENT_GC类型的消息被阻止,这显然很糟糕。
我发现这个链接:http://stackoverflow.com/questions/14187716/is-achartengine-ready-for-realtime-graphing表明内存分配问题仍然存在于achartengine中,但最后一个版本应足以显示一些实时1000点的图形。即使我将数据更新设置为1秒而不是100毫秒,我仍然会遇到视觉故障
正确的行为:4张图表实时更新(两张图表首先是3个系列,两张图片是1张系列) http://s903.photobucket.com/user/bperreno/media/correct_zpsebd731f6.png.html?sort=3&o=1
错误:图表2中的红色系列显示在其他图表上 http://s903.photobucket.com/user/bperreno/media/wrong1_zpsd33eec83.png.html?sort=3&o=0
数据正在实时移动,但有时,第二张图片中显示的故障发生,一两帧,然后消失。 有没有人知道如何,至少,修复显示问题?也许希望告诉我如何解决achartengine过度使用垃圾收集的问题
非常感谢!
编辑04.12.2013 我现在使用achartengine源而不是.jar,所以我可以查看值。看到“setInScroll”不起作用后,我看了一下代码。在GraphicalView.onDraw方法中,我比较了setInScroll为true或false时使用的值。
int top = mRect.top;
int left = mRect.left;
int width = mRect.width();
int height = mRect.getheight();
if(mRenderer.isInScroll())
{
top = 0;
left = 0;
width = getMeasuredWidth();
height = getMeasuredHeight();
}
从mRect或getMeasuredXXX()传来的值始终相同。在这两种情况下,顶部和左侧都是零。所以(至少在这种情况下),inScroll = true无效
答案 0 :(得分:1)
好的,我正在回答我的问题:
经过几次测试后,我发现当在achartengine lib上执行太多操作时,这是一个很容易出现的问题。
繁重的行动似乎包括
所以我能做的最好的办法就是将X max位置设置得更远,所以我只需要偶尔更新一次(例如每秒一次)
向库中添加允许一次删除多个点的方法也很有用。删除当前是O(n)并且一个接一个地删除m个点因此是O(nm)。
请注意,比我的galaxy S2更强大的设备(如S4mini)可以处理显示而不会出现故障。但效率仍然是更多图表和更高频率的问题。
为4个图表达到10 FPS并不是我真正称之为实时的
答案 1 :(得分:0)
通过调用显示问题可能会消失:
mRenderer.setInScroll(true);
您没有提到您正在使用的AChartEngine版本。