- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我目前正在使用 achartengine 来显示使用蓝牙接收的实时数据。数据在特定线程上正确收集,并以 Bundle 的形式每隔 100 毫秒发送到我的主要 Activity 。
主要 Activity 包含来自 achartengine 库(LineChart 类)的 4 个图表,使用 GraphicalView 添加到其 View
总而言之,每隔 100 毫秒,我的主要 Activity 就会收到一个回调,并且
调用重绘时,数据集不会更新,直到下一个包从另一个线程到达,即 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 最大位置设置得更远,所以我只需要偶尔更新一次(例如每秒一次)
向库中添加一种允许一次删除多个点的方法也很有用。一个删除目前是 O(n),一个接一个地删除 m 个点因此是 O(nm)。
请注意,比我的 galaxy S2 更强大的设备(如 S4mini)可以毫无故障地处理显示。但是对于更多的图和更高的频率,效率仍然是一个问题。
4 个图表达到 10 FPS 并不是我所谓的实时
关于android - aChartEngine:如何显示实时更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20317244/
我查看了网站上的一些问题,但还没有完全弄清楚我做错了什么。我有一些这样的代码: var mongoose = require('mongoose'), db = mongoose.connect('m
基本上,根据 this bl.ocks,我试图在开始新序列之前让所有 block 都变为 0。我认为我需要的是以下顺序: 更新为0 退出到0 更新随机数 输入新号码 我尝试通过添加以下代码块来遵循上述
我试图通过使用随机数在循环中设置 JSlider 位置来模拟“赛马”的投注结果。我的问题是,当然,我无法在线程执行时更新 GUI,因此我的 JSlider 似乎没有在竞赛,它们从头到尾都在运行。我尝试
该功能非常简单: 变量:$table是正在更新的表$fields 是表中的字段,$values 从帖子生成并放入 $values 数组中而$where是表的索引字段的id值$indxfldnm 是索引
让我们想象一个环境:有一个数据库客户端和一个数据库服务器。数据库客户端可以是 Java 程序或其他程序等;数据库服务器可以是mysql、oracle等。 需求是在数据库服务器上的一个表中插入大量记录。
在我当前的应用程序中,我正在制作一个菜单结构,它可以递归地创建自己的子菜单。然而,由于这个原因,我发现很难也允许某种重新排序方法。大多数应用程序可能只是通过“排序”列进行排序,但是在这种情况下,尽管这
Provisioning Profile 有 key , key 链依赖于它。我想知道 key 什么时候会改变。 Key will change after renew Provisioning Pr
截至目前,我在\server\publications.js 中有我的 MongoDB“选择”,例如: Meteor.publish("jobLocations", function () { r
我读到 UI 应该始终在主线程上更新。但是,当谈到实现这些更新的首选方法时,我有点困惑。 我有各种函数可以执行一些条件检查,然后使用结果来确定如何更新 UI。我的问题是整个函数应该在主线程上运行吗?应
我在代理后面,我无法构建 Docker 镜像。 我试过 FROM ubuntu , FROM centos和 FROM alpine ,但是 apt-get update/yum update/apk
我构建了一个 Java 应用程序,它向外部授权客户端公开网络服务。 Web 服务使用带有证书身份验证的 WS-security。基本上我们充当自定义证书颁发机构 - 我们在我们的服务器上维护一个 ja
因此,我有时会在上传新版本时使用 app_offline.htm 使应用程序离线。 但是,当我上传较大的 dll 时,我收到黄色错误屏幕,指出无法加载 dll。 这似乎与我对 app_offline.
我刚刚下载了 VS Apache Cordova Tools Update 5,但遇到了 Node 和 NPM 的问题。我使用默认的空白 cordova 项目进行测试。 版本 如果我在 VS 项目中对
所以我有一个使用传单库实例化的 map 对象。 map 实例在单独的模板中创建并以这种方式路由:- var app = angular.module('myApp', ['ui', 'ngResour
我使用较早的 Java 6 u 3 获得的帧速率是新版本的两倍。很奇怪。谁能解释一下? 在 Core 2 Duo 1.83ghz 上,集成视频(仅使用一个内核)- 1500(较旧的 java)与 70
我正在使用 angular 1.2 ng-repeat 创建的 div 也包含 ng-click 点击时 ng-click 更新 $scope $scope 中的变化反射(reflect)在使用 $a
这些方法有什么区别 public final void moveCamera(CameraUpdate更新)和public final void animateCamera (CameraUpdate
我尝试了另一篇文章中某人评论中关于如何将树更改为列表的建议。但是,我在某处(或某物)有未声明的变量,所以我列表中的值是 [_G667, _G673, _G679],而不是 [5, 2, 6],这是正确
实现以下场景的最佳方法是什么? 我需要从java应用程序调用/查询包含数百万条记录的数据库表。然后,对于表中的每条记录,我的应用程序应该调用第三方 API 并获取状态字段作为响应。然后我的应用程序应该
只是在编写一些与 java 图形相关的代码,这是我今天的讲座中的非常简单的示例。不管怎样,互联网似乎说更新不会被系统触发器调用,例如调整框架大小等。在这个例子中,更新是由这样的触发器调用的(因此当我只
我是一名优秀的程序员,十分优秀!