aChartEngine:如何显示实时更新

时间:2013-12-01 21:03:59

标签: android real-time achartengine

我目前正在使用achartengine来显示使用蓝牙接收的实时数据。 数据在特定线程上正确收集,并在Bundle中100ms发送到我的主要活动。

主要活动包含来自achartengine库(LineChart类)的4个图表,使用GraphicalView添加到其视图

总而言之,我的主要活动每100毫秒收到一次回调和

  • 复制捆绑包中的数据
  • 将其添加到4个不同的数据集(XYMultipleSeriesDataset)
  • 在包含数据集的4个图表上调用repaint()

调用重绘时,数据集将不会更新,直到下一个包从其他线程到达,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无效

2 个答案:

答案 0 :(得分:1)

好的,我正在回答我的问题:

经过几次测试后,我发现当在achartengine lib上执行太多操作时,这是一个很容易出现的问题。

繁重的行动似乎包括

  • 删除一个点
  • 设置X最大显示值
  • 刷新

所以我能做的最好的办法就是将X max位置设置得更远,所以我只需要偶尔更新一次(例如每秒一次)

向库中添加允许一次删除多个点的方法也很有用。删除当前是O(n)并且一个接一个地删除m个点因此是O(nm)。

请注意,比我的galaxy S2更强大的设备(如S4mini)可以处理显示而不会出现故障。但效率仍然是更多图表和更高频率的问题。

为4个图表达到10 FPS并不是我真正称之为实时的

答案 1 :(得分:0)

通过调用显示问题可能会消失:

mRenderer.setInScroll(true);

您没有提到您正在使用的AChartEngine版本。