標籤:
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;//一屏顯示測量次數@Overrideprotected 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() {//有三個返回參數的監聽器@Overridepublic void onSensorChanged(SensorEvent event) {// TODO Auto-generated method stubxText.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裡面的平均值,並顯示}@Overridepublic 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));}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.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 blocke.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() {@Overridepublic void run() {// TODO Auto-generated method stubSystem.out.println("線程已經啟動");while(true){try {Thread.sleep(INTERVAL);//沒隔固定時間求平均數} catch (InterruptedException e) {// TODO Auto-generated catch blocke.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(){//更新textViewif(sumText==null)return;sumText.setText(AVERAGE+"");}/** * 每隔固定時間給平均值賦值,並且更新圖表的操作 * @author love fang * */class AveHandler extends Handler{@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsetAverageView();//顯示平均值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 blocke.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使用AChartEngine製作動態心電圖效果