gpt4 book ai didi

android - aChartEngine:如何显示实时更新

转载 作者:太空宇宙 更新时间:2023-11-03 10:47:34 26 4
gpt4 key购买 nike

我目前正在使用 achartengine 来显示使用蓝牙接收的实时数据。数据在特定线程上正确收集,并以 Bundle 的形式每隔 100 毫秒发送到我的主要 Activity 。

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

总而言之,每隔 100 毫秒,我的主要 Activity 就会收到一个回调,并且

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

调用重绘时,数据集不会更新,直到下一个包从另一个线程到达,即 100 毫秒后。我假设 100 毫秒应该足以重绘 View 。

我在这里发布了一个包含数据集和 View 的类的小版本,以及实例化和使用它的主要 Activity 的一部分

/** 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

数据是实时移动的,但时不时会出现第二张图所示的故障,持续一两帧,然后就消失了。有谁知道如何至少解决显示问题?也许希望能告诉我如何解决图表引擎过度使用垃圾回收的问题

非常感谢!

编辑 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 没有效果

最佳答案

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

经过多次测试,发现主要是charartengine lib操作过多导致的并发问题。

繁重的操作似乎包括

  • 删除一个点
  • 设置X最大显示值
  • 提神

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

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

请注意,比我的 galaxy S2 更强大的设备(如 S4mini)可以毫无故障地处理显示。但是对于更多的图和更高的频率,效率仍然是一个问题。

4 个图表达到 10 FPS 并不是我所谓的实时

关于android - aChartEngine:如何显示实时更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20317244/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com