首页 文章

aChartEngine:如何显示实时更新

提问于
浏览
4

我目前正在使用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过度使用垃圾收集

非常感谢!

EDIT 04.12.2013 我'm now using the achartengine sources instead of the .jar so I'能够查看这些值 . 看到"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 回答

  • 1

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

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

    繁重的行动似乎包括在内

    • 删除一个点

    • 设置X最大显示值

    • 令人耳目一新

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

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

    请注意,比我的galaxy S2更强大的设备(如S4mini)处理显示器没有毛刺 . 但效率仍然是更多图表和更高频率的问题 .

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

  • 0

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

    mRenderer.setInScroll(true);
    

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

相关问题