Android使用AChartEngine制作动态心电图效果
2017-01-09 22:14
369 查看
From:http://lcbk.net/4183.html
AChartEngine是一个很强大的图表引擎,我在上学的时候就接触过,并且利用它做了一个传感器的应用,想想现在也很久远了,今天就把这个app的源码贴出来供其他人研究这款引擎。app的效果如下:
g ithub项目地址: https://github.com/AlexZhuo/SensorMonitor
此APP的主要特性如下:
1、支持手机传感器数量探测,支持加速度,磁场,距离等传感器数据的实时获取
2、使用了一些github上的第三方控件来美化UI,
3、支持全屏显示
4、通过旧版的xUtils 2实现了数据库实时记录
5、通过POI来将数据库的数据导出为excel,方便以后分析。
AChartEngine根据事先准备好的数据进行绘图,也就是说是一帧一帧的进行绘制,如果我们想动态生成波浪图并让坐标轴随之移动其实也不难,只需要隔一段时间在旧数据上添加数据并刷新一下view即可。
想要在项目中使用这个图标引擎,AChartEngine的jar包的下载地址:
http://repository-achartengine.forge.cloudbees.com/snapshot/org/achartengine/achartengine/1.2.0/
注意版本,以后说不定还会出新版。
下面把控制绘图的代码贴一下,原理其实不是很复杂,github上有全部app的代码,如果你喜欢也可以在原来基础上帮我完善一下(学生时代第一个上线项目,想想还有点小激动)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | import java.util.List; import org.achartengine.ChartFactory; import org.achartengine.GraphicalView; import org.achartengine.chart.PointStyle; import org.achartengine.model.XYMultipleSeriesDataset; import org.achartengine.model.XYSeries; import org.achartengine.renderer.XYMultipleSeriesRenderer; import org.achartengine.renderer.XYSeriesRenderer; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.Paint.Align; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Vibrator; import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; import android.widget.TextView; import com.lidroid.xutils.DbUtils; import com.lidroid.xutils.db.sqlite.Selector; import com.lidroid.xutils.exception.DbException; import com.situ.model.Accelerate_info; public class AccelActivity extends Activity{ public SensorManager sensorManager; public Sensor accelSensor ; TextView xText ; TextView yText ; TextView zText ; TextView sumText; TextView danWei ; TextView title; private Vibrator vibrator; SensorEventListener threeParamListener; SensorEventListener oneParamListener; SensorEventListener twoParamListener; Handler avgHandler; Thread avgThread; int sensor_id = 0; //图表相关 private XYSeries series; private XYMultipleSeriesDataset mDataset; private GraphicalView chart; private XYMultipleSeriesRenderer renderer; private Context context; private int yMax = 20;//y轴最大值,根据不同传感器变化 private int xMax = 100;//一屏显示测量次数 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_accel); sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); avgHandler = new AveHandler(); //给控件实例化 if(xText==null){ findViews(); } Intent intent = getIntent(); avgThread = new Thread(runnable);//定期更新平均值的线程启动 avgThread.start(); //初始化各个监听器 initListeners(); switch (wtd) { case Sensor.TYPE_ACCELEROMETER: title.setText("加速度传感器"); danWei.setText("N/M^2"); accelSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); sensorManager.registerListener(threeParamListener, accelSensor, sensorManager.SENSOR_DELAY_UI); yMax = 20; sensor_id = 1; break; default: break; } //初始化图表 initChart("*0.5s", danWei.getText().toString(),0,xMax,0,yMax); } /** * 抓取view中文本控件的函数 */ private void findViews(){ xText = (TextView) findViewById(R.id.xAxis); yText = (TextView) findViewById(R.id.yAxis); zText = (TextView) findViewById(R.id.zAxis); sumText = (TextView) findViewById(R.id.sum); danWei = (TextView) findViewById(R.id.danWei); title = (TextView) findViewById(R.id.title); } /** * 初始化各类监听器 */ private void initListeners() { threeParamListener = new SensorEventListener() {//有三个返回参数的监听器 @Override public void onSensorChanged(SensorEvent event) { // TODO Auto-generated method stub xText.setText(event.values[0]+""); yText.setText(event.values[1]+""); zText.setText(event.values[2]+""); double sum = threeDimenToOne(event.values[0], event.values[1], event.values[2]); giveAverage(sum);//将当前测量的结果写入buffer,然后定期求buffer里面的平均值,并显示 } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // TODO Auto-generated method stub } }; } /** * 初始化图表 */ private void initChart(String xTitle,String yTitle,int minX,int maxX,int minY,int maxY){ //这里获得main界面上的布局,下面会把图表画在这个布局里面 LinearLayout layout = (LinearLayout)findViewById(R.id.chart); //这个类用来放置曲线上的所有点,是一个点的集合,根据这些点画出曲线 series = new XYSeries("历史曲线"); //创建一个数据集的实例,这个数据集将被用来创建图表 mDataset = new XYMultipleSeriesDataset(); //将点集添加到这个数据集中 mDataset.addSeries(series); //以下都是曲线的样式和属性等等的设置,renderer相当于一个用来给图表做渲染的句柄 int lineColor = Color.BLACK; PointStyle style = PointStyle.CIRCLE; renderer = buildRenderer(lineColor, style, true); //设置好图表的样式 setChartSettings(renderer, xTitle,yTitle, minX, maxX, //x轴最小最大值 minY, maxY, //y轴最小最大值 Color.BLACK, //坐标轴颜色 Color.WHITE//标签颜色 ); //生成图表 chart = ChartFactory.getLineChartView(this, mDataset, renderer); //将图表添加到布局中去 layout.addView(chart, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); if(threeParamListener!=null){ sensorManager.unregisterListener(threeParamListener); } if(oneParamListener!=null){ sensorManager.unregisterListener(oneParamListener); } if(avgThread!=null) avgThread.interrupt(); DbUtils db = DbUtils.create(getApplicationContext());//xUtils框架 try { List<Accelerate_info> list = db.findAll(Selector.from(Accelerate_info.class).where("sensor", "=", sensor_id));//看看一共写入了数据库多少数据 System.out.println("数量是"+list.size()); } catch (DbException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 根据三个坐标向量求和向量的模 * @param x * @param y * @param z * @return */ public static double threeDimenToOne(double x,double y,double z){ return Math.sqrt(x*x+y*y+z*z); } public int index = 0;//指示这段时间一共写入了多少个数据 //在这里可以设置缓冲区的长度,用于求平均数 double[] buffer = new double[500];//半秒钟最多放500个数 public int INTERVAL = 500;//每半秒求一次平均值 public double AVERAGE = 0;//存储平均值 /** * 一个子线程,没隔固定时间计算这段时间的平均值,并给textView赋值 */ Runnable runnable = new Runnable() { @Override public void run() { // TODO Auto-generated method stub System.out.println("线程已经启动"); while(true){ try { Thread.sleep(INTERVAL);//没隔固定时间求平均数 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); avgThread = new Thread(runnable); avgThread.start(); } if(index!=0){ double sum = 0; for (int i=0;i<index;i++) { sum+=buffer[i]; } AVERAGE = sum/new Double(index); index=0;//让下标恢复 } avgHandler.sendEmptyMessage(1);//提示handler更新ui } } }; /** * 更新平均值的显示值 */ public void setAverageView(){//更新textView if(sumText==null)return; sumText.setText(AVERAGE+""); } /** * 每隔固定时间给平均值赋值,并且更新图表的操作 * @author love fang * */ class AveHandler extends Handler{ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub setAverageView();//显示平均值 updateChart();//更新图表 //把当前值存入数据库 DbUtils db = DbUtils.create(getApplicationContext()); Accelerate_info accelerate_info = new Accelerate_info(System.currentTimeMillis(), AVERAGE, sensor_id); try { db.save(accelerate_info);//将当前平均值存入数据库 } catch (DbException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("保存失败"); } } } /** * 接受当前传感器的测量值,存到缓存区中去,并将下标加一 * @param data */ public void giveAverage(double data){ buffer[index]=data; index++; } /** *设置图表曲线样式 **/ protected XYMultipleSeriesRenderer buildRenderer(int color, PointStyle style, boolean fill) { XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); //设置图表中曲线本身的样式,包括颜色、点的大小以及线的粗细等 XYSeriesRenderer r = new XYSeriesRenderer(); r.setColor(color); r.setPointStyle(style); r.setFillPoints(fill); r.setLineWidth(2);//这是线宽 renderer.addSeriesRenderer(r); return renderer; } /** * 初始化图表 * @param renderer * @param xTitle * @param yTitle * @param xMin * @param xMax * @param yMin * @param yMax * @param axesColor * @param labelsColor */ protected void setChartSettings(XYMultipleSeriesRenderer renderer, String xTitle, String yTitle, double xMin, double xMax, double yMin, double yMax, int axesColor, int labelsColor) { //有关对图表的渲染可参看api文档 renderer.setChartTitle(title.getText().toString());//设置标题 renderer.setXAxisMin(xMin);//设置x轴的起始点 renderer.setXAxisMax(xMax);//设置一屏有多少个点 renderer.setYAxisMin(yMin); renderer.setYAxisMax(yMax); renderer.setBackgroundColor(Color.BLACK); renderer.setLabelsColor(Color.YELLOW); renderer.setAxesColor(axesColor); renderer.setLabelsColor(labelsColor); renderer.setShowGrid(true); renderer.setGridColor(Color.BLUE);//设置格子的颜色 renderer.setXLabels(20);//没有什么卵用 renderer.setYLabels(20);//把y轴刻度平均分成多少个 renderer.setXTitle(xTitle);//x轴的标题 renderer.setYTitle(yTitle);//y轴的标题 renderer.setYLabelsAlign(Align.RIGHT); renderer.setPointSize((float) 2); renderer.setShowLegend(false); } int[] xv = new int[1000];//用来显示的数据 double[] yv = new double[1000]; private int addX = -1; private double addY = 0; /** * 更新图表的函数,其实就是重绘 */ private void updateChart() { //设置好下一个需要增加的节点 addY = AVERAGE;//需要增加的值 //移除数据集中旧的点集 mDataset.removeSeries(series); //判断当前点集中到底有多少点,因为屏幕总共只能容纳100个,所以当点数超过100时,长度永远是100 //每一个新点坐标都后移一位 series.add(addX++, addY);//最重要的一句话,以xy对的方式往里放值 if(addX>100){//如果超出了屏幕边界,实现坐标轴自动移动的方法 renderer.setXAxisMin(addX-100);//显示范围为100 renderer.setXAxisMax(addX); } //重要:在数据集中添加新的点集 mDataset.addSeries(series); //视图更新,没有这一步,曲线不会呈现动态 //如果在非UI主线程中,需要调用postInvalidate(),具体参考api chart.invalidate(); } } |
相关文章推荐
- Android学习笔记-Android6.0运行时权限(续)
- Android控件学习之Spinner
- Android的目录结构
- Android爬坑之旅之FileProvider(Failed to find configured root that contains)
- Android 点击图片放大展示 展示中可调节图片的缩放显示 图片查看器
- ViewPager 详解(四)—-自主实现滑动指示条
- Android广播机制(转)
- 解决Android 7.0 (Android N)中 Android Device Monitor(DDMS)的 File Explorer不显示目录树的问题
- Android 多渠道打包提速
- 【Android】触摸事件相关方法总结
- Android学习笔记-Android6.0运行时权限
- Android注解利器:ButterKnife 的基本使用
- Android6.0 MediaScanner多媒体文件开机扫描及修改默认铃声(二)
- 理解Android的startservice和bindservice(转)
- Android 编写硬件抽象层(HAL)模块接口
- 【Android 基础】ViewPager中使用ImageLoader
- android开发实现静默安装(root权限)
- Android 实现文件的单线程断点续传下载
- Android利用Achartengine实现实时曲线图
- AndroidMainfest.xml详解——<action>