使用AChartEngine画动态曲线图
AChartEngine是一个开源的Android图表库,可以用来画折线图、平滑折线图、饼图、直方图等等。使用简单,功能强大。
AChartEngine官网:http://www.achartengine.org/
AChartEngine库文件:http://repository-achartengine.forge.cloudbees.com/snapshot/org/achartengine/achartengine/1.2.0/
库文件直接导入就可以使用了。
网上介绍AChartEngine的文章很多,不过大多数都只是贴代码,没有讲述相关类及其原理。最新项目要画折线图,搜集了很多资料才弄清楚原理,在此总结整理,希望给读者一个较清晰的理解,少走弯路。由于我的项目中只用到了折线图,在此我只讲解折线图,其他图标原理也是类似的。
AChartEngine官方提供的Demo中,所有的图表都是使用Intent的方式来画的,在实际项目中,这种方式不够灵活,因为一个页面不可能只显示图表,或者不止显示一个图表。
AChartEngine给我们提供了GraphicalView这个类,可以灵活的在任何位置插入图表,非常的方便。
下面对相关类进行讲解:
GraphicalView:图表控件,是一个基本类,也是一个容器控件,所有的图表都在此控件中呈现;
ChartFactory:工厂类,通过此类来构建不同的图表对象,比如LineChart(折线图)、CubeLineChart(圆滑折线图)、PieChart(饼图)等;
XYMultipleSeriesDataset:数据集容器,在此类中存放多条曲线的数据集合
XYMultipleSeriesRenderer:渲染器容器,此类初始化坐标系,网格,标题等,还用来存放多条曲线的渲染器
XYSeries:数据集,存放曲线的数据
XYSeriesRenderer:渲染器,存放曲线的参数,比如线条颜色,描点大小等
以下以一个简单的动态折线图来展现相关类的调用,其中ChartService是我自己定义的一个类,可以直接使用到实际项目中:
效果图
MainActivity:
1 package com.example.mychartdemo; 2 3 import java.util.Timer; 4 import java.util.TimerTask; 5 6 import org.achartengine.GraphicalView; 7 8 import com.ivan.chart.ChartService; 9 10 import android.app.Activity; 11 import android.graphics.Color; 12 import android.os.Bundle; 13 import android.os.Handler; 14 import android.os.Message; 15 import android.view.Menu; 16 import android.view.ViewGroup.LayoutParams; 17 import android.widget.LinearLayout; 18 19 public class MainActivity extends Activity { 20 21 private LinearLayout mLeftCurveLayout;//存放左图表的布局容器 22 private LinearLayout mRightCurveLayout;//存放右图表的布局容器 23 private GraphicalView mView, mView2;//左右图表 24 private ChartService mService, mService2; 25 private Timer timer; 26 27 @Override 28 protected void onCreate(Bundle savedInstanceState) { 29 super.onCreate(savedInstanceState); 30 setContentView(R.layout.activity_main); 31 32 mLeftCurveLayout = (LinearLayout) findViewById(R.id.left_temperature_curve); 33 mRightCurveLayout = (LinearLayout) findViewById(R.id.right_temperature_curve); 34 35 mService = new ChartService(this); 36 mService.setXYMultipleSeriesDataset("左温度曲线"); 37 mService.setXYMultipleSeriesRenderer(100, 100, "左温度曲线", "时间", "温度", 38 Color.RED, Color.RED, Color.RED, Color.BLACK); 39 mView = mService.getGraphicalView(); 40 41 mService2 = new ChartService(this); 42 mService2.setXYMultipleSeriesDataset("右温度曲线"); 43 mService2.setXYMultipleSeriesRenderer(100, 100, "右温度曲线", "时间", "温度", 44 Color.RED, Color.RED, Color.RED, Color.BLACK); 45 mView2 = mService2.getGraphicalView(); 46 47 //将左右图表添加到布局容器中 48 mLeftCurveLayout.addView(mView, new LayoutParams( 49 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 50 mRightCurveLayout.addView(mView2, new LayoutParams( 51 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 52 53 timer = new Timer(); 54 timer.schedule(new TimerTask() { 55 @Override 56 public void run() { 57 handler.sendMessage(handler.obtainMessage()); 58 } 59 }, 10, 1000); 60 } 61 62 @Override 63 public boolean onCreateOptionsMenu(Menu menu) { 64 getMenuInflater().inflate(R.menu.main, menu); 65 return true; 66 } 67 68 private int t = 0; 69 private Handler handler = new Handler() { 70 @Override 71 //定时更新图表 72 public void handleMessage(Message msg) { 73 mService.updateChart(t, Math.random() * 100); 74 mService2.updateChart(t, Math.random() * 100); 75 t+=5; 76 } 77 }; 78 79 @Override 80 protected void onDestroy() { 81 super.onDestroy(); 82 if (timer != null) { 83 timer.cancel(); 84 } 85 } 86 87 }
ChartService:
1 package com.ivan.chart; 2 3 import java.util.List; 4 5 import org.achartengine.ChartFactory; 6 import org.achartengine.GraphicalView; 7 import org.achartengine.chart.PointStyle; 8 import org.achartengine.model.XYMultipleSeriesDataset; 9 import org.achartengine.model.XYSeries; 10 import org.achartengine.renderer.XYMultipleSeriesRenderer; 11 import org.achartengine.renderer.XYSeriesRenderer; 12 import android.content.Context; 13 import android.graphics.Color; 14 import android.graphics.Paint.Align; 15 16 public class ChartService { 17 18 private GraphicalView mGraphicalView; 19 private XYMultipleSeriesDataset multipleSeriesDataset;// 数据集容器 20 private XYMultipleSeriesRenderer multipleSeriesRenderer;// 渲染器容器 21 private XYSeries mSeries;// 单条曲线数据集 22 private XYSeriesRenderer mRenderer;// 单条曲线渲染器 23 private Context context; 24 25 public ChartService(Context context) { 26 this.context = context; 27 } 28 29 /** 30 * 获取图表 31 * 32 * @return 33 */ 34 public GraphicalView getGraphicalView() { 35 mGraphicalView = ChartFactory.getCubeLineChartView(context, 36 multipleSeriesDataset, multipleSeriesRenderer, 0.1f); 37 return mGraphicalView; 38 } 39 40 /** 41 * 获取数据集,及xy坐标的集合 42 * 43 * @param curveTitle 44 */ 45 public void setXYMultipleSeriesDataset(String curveTitle) { 46 multipleSeriesDataset = new XYMultipleSeriesDataset(); 47 mSeries = new XYSeries(curveTitle); 48 multipleSeriesDataset.addSeries(mSeries); 49 } 50 51 /** 52 * 获取渲染器 53 * 54 * @param maxX 55 * x轴最大值 56 * @param maxY 57 * y轴最大值 58 * @param chartTitle 59 * 曲线的标题 60 * @param xTitle 61 * x轴标题 62 * @param yTitle 63 * y轴标题 64 * @param axeColor 65 * 坐标轴颜色 66 * @param labelColor 67 * 标题颜色 68 * @param curveColor 69 * 曲线颜色 70 * @param gridColor 71 * 网格颜色 72 */ 73 public void setXYMultipleSeriesRenderer(double maxX, double maxY, 74 String chartTitle, String xTitle, String yTitle, int axeColor, 75 int labelColor, int curveColor, int gridColor) { 76 multipleSeriesRenderer = new XYMultipleSeriesRenderer(); 77 if (chartTitle != null) { 78 multipleSeriesRenderer.setChartTitle(chartTitle); 79 } 80 multipleSeriesRenderer.setXTitle(xTitle); 81 multipleSeriesRenderer.setYTitle(yTitle); 82 multipleSeriesRenderer.setRange(new double[] { 0, maxX, 0, maxY });//xy轴的范围 83 multipleSeriesRenderer.setLabelsColor(labelColor); 84 multipleSeriesRenderer.setXLabels(10); 85 multipleSeriesRenderer.setYLabels(10); 86 multipleSeriesRenderer.setXLabelsAlign(Align.RIGHT); 87 multipleSeriesRenderer.setYLabelsAlign(Align.RIGHT); 88 multipleSeriesRenderer.setAxisTitleTextSize(20); 89 multipleSeriesRenderer.setChartTitleTextSize(20); 90 multipleSeriesRenderer.setLabelsTextSize(20); 91 multipleSeriesRenderer.setLegendTextSize(20); 92 multipleSeriesRenderer.setPointSize(2f);//曲线描点尺寸 93 multipleSeriesRenderer.setFitLegend(true); 94 multipleSeriesRenderer.setMargins(new int[] { 20, 30, 15, 20 }); 95 multipleSeriesRenderer.setShowGrid(true); 96 multipleSeriesRenderer.setZoomEnabled(true, false); 97 multipleSeriesRenderer.setAxesColor(axeColor); 98 multipleSeriesRenderer.setGridColor(gridColor); 99 multipleSeriesRenderer.setBackgroundColor(Color.WHITE);//背景色 100 multipleSeriesRenderer.setMarginsColor(Color.WHITE);//边距背景色,默认背景色为黑色,这里修改为白色 101 mRenderer = new XYSeriesRenderer(); 102 mRenderer.setColor(curveColor); 103 mRenderer.setPointStyle(PointStyle.CIRCLE);//描点风格,可以为圆点,方形点等等 104 multipleSeriesRenderer.addSeriesRenderer(mRenderer); 105 } 106 107 /** 108 * 根据新加的数据,更新曲线,只能运行在主线程 109 * 110 * @param x 111 * 新加点的x坐标 112 * @param y 113 * 新加点的y坐标 114 */ 115 public void updateChart(double x, double y) { 116 mSeries.add(x, y); 117 mGraphicalView.repaint();//此处也可以调用invalidate() 118 } 119 120 /** 121 * 添加新的数据,多组,更新曲线,只能运行在主线程 122 * @param xList 123 * @param yList 124 */ 125 public void updateChart(List<Double> xList, List<Double> yList) { 126 for (int i = 0; i < xList.size(); i++) { 127 mSeries.add(xList.get(i), yList.get(i)); 128 } 129 mGraphicalView.repaint();//此处也可以调用invalidate() 130 } 131 }
布局文件:activity_main:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 android:paddingBottom="@dimen/activity_vertical_margin" 7 android:paddingLeft="@dimen/activity_horizontal_margin" 8 android:paddingRight="@dimen/activity_horizontal_margin" 9 android:paddingTop="@dimen/activity_vertical_margin" 10 tools:context=".MainActivity" > 11 12 <LinearLayout 13 android:layout_width="match_parent" 14 android:layout_height="0dp" 15 android:layout_marginBottom="20dp" 16 android:layout_weight="1" 17 android:orientation="vertical" > 18 19 <TextView 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" 22 android:layout_marginBottom="10dp" 23 android:text="左温度曲线" /> 24 25 <LinearLayout 26 android:id="@+id/left_temperature_curve" 27 android:layout_width="match_parent" 28 android:layout_height="match_parent" 29 android:orientation="horizontal" > 30 </LinearLayout> 31 </LinearLayout> 32 33 <LinearLayout 34 android:layout_width="match_parent" 35 android:layout_height="0dp" 36 android:layout_weight="1" 37 android:orientation="vertical" > 38 39 <TextView 40 android:layout_width="wrap_content" 41 android:layout_height="wrap_content" 42 android:layout_marginBottom="10dp" 43 android:text="右温度曲线" /> 44 45 <LinearLayout 46 android:id="@+id/right_temperature_curve" 47 android:layout_width="match_parent" 48 android:layout_height="match_parent" 49 android:orientation="horizontal" > 50 </LinearLayout> 51 </LinearLayout> 52 53 </LinearLayout>
详细说明见注释。
AChartEngine功能真的很强大,之前一直想要自己来画曲线图,浪费了几天功夫。所以说,站在巨人的肩膀上是很重要的。对于程序员,有一句很重要的话,叫做不要重复造轮子。但是,要使用别人的轮子,必须先要理解透彻,否则很难实现漂亮的效果。
由于笔者水平有限,如有错漏之处在所难免,欢迎读者批评指正,感激不尽!
原创文章,转载请注明出处。
全文完