您的位置:首页 > 移动开发 > Android开发

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();

}

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: