Android使用AChartEngine制作动态心电图效果

AChartEngine是一个很强大的图表引擎,我在上学的时候就接触过,并且利用它做了一个传感器的应用,想想现在也很久远了,今天就把这个app的源码贴出来供其他人研究这款引擎。

app的效果如下:

github项目地址: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的代码,如果你喜欢也可以在原来基础上帮我完善一下(学生时代第一个上线项目,想想还有点小激动)。

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();
	    }
}
时间: 2024-10-13 08:00:38

Android使用AChartEngine制作动态心电图效果的相关文章

50个Android开发人员必备UI效果源码[转载]

50个Android开发人员必备UI效果源码[转载] http://blog.csdn.net/qq1059458376/article/details/8145497 Android 仿微信之主页面实现篇Android 仿微信之界面导航篇Android 高仿QQ 好友分组列表Android 高仿QQ 界面滑动效果Android 高仿QQ 登陆界面Android 对Path的旋转效果的拓展Android高仿360安全卫士布局源码Android SlidingDrawer 滑动抽屉效果Androi

quick-cocos2d-x学习笔记【6】——制作自定义效果按钮菜单

前面提到的是基本的菜单使用,还是很容易的,不过我们在商业产品中,经常看到的按钮都是非常好看,不光光是图片做得精美,而且动画效果也很棒.Candy Crash都玩过吧,看它们那个按钮,真的像果冻一样,效果确实很赞,所以我们也来做个,当然不是它那个效果,而且点击之后有一个抖动的效果.像这样, 好了,开始做个吧,我这个也是搬了一下coinflip中的按钮代码,算是学习一下. 创建一个views文件夹,里面创建一个MyButton.lua文件,这个就是我们的自定义按钮类了.实现的原理比较简单,在ui.n

android shape图形优化Button效果

android shape可以让我们通过定义xml文件的方式创建图形,当然只能实现一些比较简单的图形(圆形,矩形,椭圆,线段),但是已经相当不错了,通过shape创建的图形作为控件的背景已经基本可以满足我的简单需求了,而且通过shape创建的图形可以适配各种屏幕. 下面就用shape定义的图形来优化Button的整体效果. 定义主布局文件activity_main.xml: 1 <RelativeLayout xmlns:android="http://schemas.android.co

利用div和css制作三角形效果

利用div和css制作三角形效果:本章节介绍一下如何利用div和css实现三角形效果,虽然看起来表神奇,但是原理是非常的简单.代码如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author" content="http://www.softwhy.com/" /> <title>蚂蚁

android利用jdk制作签名

Apk签名首先要有一个keystore的签名用的文件. keystore是由jdk自带的工具keytool生成的.具体生成方式参考一下: 开始->运行->cmd->cd 到你安装的jdk的目录这里我是 C:\Program Files\Java\jdk1.6.0_10\bin 然后输入:keytool -genkey -alias lvmama.keystore -keyalg RSA -validity 10000 -keystore lvmama.keystore 下面解释下签名的参

CSDN Android客户端的制作 导航帖

弄个导航贴,把相关知识来个汇总. CSDN Android的客户端的效果图: 分别通过以下博客进行详细的讲解: 1.Android 使用Fragment,ViewPagerIndicator 制作csdn app主要框架 主要使用ViewPageIndicator , FragmentPagerAdapter ,ViewPager对主框架的制作: 2.抓取csdn上的各类别的文章 (制作csdn app 二) 使用Java对CSDN中我项目中需要用的数据的抓取,一方面java调试比较方便,另一方

android 仿ppt进入动画效果合集

EnterAnimation android 仿ppt进入动画效果合集, 百叶窗效果,擦除效果,盒状效果,阶梯效果,菱形效果,轮子效果,劈裂效果,棋盘效果, 切入效果,扇形展开效果,十字扩展效果,随机线条效果,向内溶解效果,圆形扩展效果, 适用于各种view和viewgroup,activity即用于页面根部viewgroup, 自定义viewgroup自动换行layout, 看效果图 Series of entrance animation effects just like ppt in A

Android三种左右滑动效果 手势识别

Android三种左右滑动效果 手势识别(转) 手势识别 1.onCreate中添加GestureDetector mGestureDetector; //监听手势事件 mGestureDetector = new GestureDetector(this, onGestureListener); 2.//实现处理事件 OnGestureListener onGestureListener = new OnGestureListener() { //添加未实现的方法 }; 3.重写onTouch

Android图标的制作

转载自:http://blog.csdn.net/caiwenfeng_for_23/article/details/12834113 Android的adt提供了android图标的制作:Android icon set. 通过这个可以新建一个图标. 1.首先,在Eclipse中某个android项目上右击打开 New - Other 视图,找到Android 2.找到Android icon set 3.然后点击Launcher icon 4.接下来点击Image,选择图片 5.根据要求设置