您的位置:首页 > 其它

aChartEngine制作双y轴(barChart+TimeChart)图表(x轴为日期/时间)

2013-11-07 20:30 471 查看
在做一个安卓项目时需要做一个x轴为时间,两个y轴,一个用折线表示的体重,一个用柱状图表示的运动量的图。找到了achart这个开源的引擎,找了一圈,没发现做类似图的教程。只好去看官方的文档,下图是引擎中图表类之间的关系图:



x坐标为时间(日期)的图表是TimeChart,他继承自折线图(LineChart),而要制作折线-柱形复合图却需要用combinedXYChart,而CombinedXyChart没有的对横坐标时间的支持。看了下源码,决定模仿TimeChart,自己扩展一个CombinedTimeChart。

先写一个支持双y轴的数据类:

public class MyTimeSeries extends XYSeries {
/**
* Builds a new date / time series.
*
* @param title
*            the series title
*/
public MyTimeSeries(String title) {
super(title);
}

public MyTimeSeries(String title, int scaleNumber) {
super(title, scaleNumber);//其实相比原来的TimeSeries只是加了这个构造函数
}
/**
* Adds a new value to the series.
*
* @param x
*            the date / time value for the X axis
* @param y
*            the value for the Y axis
*/
public synchronized void add(Date x, double y) {
super.add(x.getTime(), y);
}

protected double getPadding(double x) {
return 1;
}
}

然后写一个CombineTimeChart继承combinedXYChart:

public class CombinedTimeChart extends CombinedXYChart {
/** The constant to identify this chart type. */
public static final String TYPE = "Time";
/** The number of milliseconds in a day. */
public static final long DAY = 24 * 60 * 60 * 1000;
/** The date format pattern to be used in formatting the X axis labels. */
private String mDateFormat;
/** The starting point for labels. */
private Double mStartPoint;

/**
* Builds a new time chart instance.
*
* @param dataset the multiple series dataset
* @param renderer the multiple series renderer
*/
public CombinedTimeChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer,
String[] types) {
super(dataset, renderer, types);
}

/**
* Returns the date format pattern to be used for formatting the X axis
* labels.
*
* @return the date format pattern for the X axis labels
*/
public String getDateFormat() {
return mDateFormat;
}

/**
* Sets the date format pattern to be used for formatting the X axis labels.
*
* @param format the date format pattern for the X axis labels. If null, an
*          appropriate default format will be used.
*/
public void setDateFormat(String format) {
mDateFormat = format;
}

/**
* The graphical representation of the labels on the X axis.
*
* @param xLabels the X labels values
* @param xTextLabelLocations the X text label locations
* @param canvas the canvas to paint to
* @param paint the paint to be used for drawing
* @param left the left value of the labels area
* @param top the top value of the labels area
* @param bottom the bottom value of the labels area
* @param xPixelsPerUnit the amount of pixels per one unit in the chart labels
* @param minX the minimum value on the X axis in the chart
* @param maxX the maximum value on the X axis in the chart
*/
@Override
protected void drawXLabels(List<Double> xLabels, Double[] xTextLabelLocations, Canvas canvas,
Paint paint, int left, int top, int bottom, double xPixelsPerUnit, double minX, double maxX) {
int length = xLabels.size();
if (length > 0) {
boolean showLabels = mRenderer.isShowLabels();
boolean showGridY = mRenderer.isShowGridY();
boolean showTickMarks = mRenderer.isShowTickMarks();
DateFormat format = getDateFormat(xLabels.get(0), xLabels.get(length - 1));
for (int i = 0; i < length; i++) {
long label = Math.round(xLabels.get(i));
float xLabel = (float) (left + xPixelsPerUnit * (label - minX));
if (showLabels) {
paint.setColor(mRenderer.getXLabelsColor());
if (showTickMarks) {
canvas.drawLine(xLabel, bottom, xLabel, bottom + mRenderer.getLabelsTextSize() / 3,
paint);
}
drawText(canvas, format.format(new Date(label)), xLabel,
bottom + mRenderer.getLabelsTextSize() * 4 / 3 + mRenderer.getXLabelsPadding(),
paint, mRenderer.getXLabelsAngle());
}
if (showGridY) {
paint.setColor(mRenderer.getGridColor(0));
canvas.drawLine(xLabel, bottom, xLabel, top, paint);
}
}
}
drawXTextLabels(xTextLabelLocations, canvas, paint, true, left, top, bottom, xPixelsPerUnit,
minX, maxX);
}

/**
* Returns the date format pattern to be used, based on the date range.
*
* @param start the start date in milliseconds
* @param end the end date in milliseconds
* @return the date format
*/
private DateFormat getDateFormat(double start, double end) {
if (mDateFormat != null) {
SimpleDateFormat format = null;
try {
format = new SimpleDateFormat(mDateFormat);
return format;
} catch (Exception e) {
// do nothing here
}
}
DateFormat format = SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM);
double diff = end - start;
if (diff > DAY && diff < 5 * DAY) {
format = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT);
} else if (diff < DAY) {
format = SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM);
}
return format;
}

/**
* Returns the chart type identifier.
*
* @return the chart type
*/
public String getChartType() {
return TYPE;
}

@Override
protected List<Double> getXLabels(double min, double max, int count) {
final List<Double> result = new ArrayList<Double>();
if (!mRenderer.isXRoundedLabels()) {
if (mDataset.getSeriesCount() > 0) {
XYSeries series = mDataset.getSeriesAt(0);
int length = series.getItemCount();
int intervalLength = 0;
int startIndex = -1;
for (int i = 0; i < length; i++) {
double value = series.getX(i);
if (min <= value && value <= max) {
intervalLength++;
if (startIndex < 0) {
startIndex = i;
}
}
}
if (intervalLength < count) {
for (int i = startIndex; i < startIndex + intervalLength; i++) {
result.add(series.getX(i));
}
} else {
float step = (float) intervalLength / count;
int intervalCount = 0;
for (int i = 0; i < length && intervalCount < count; i++) {
double value = series.getX(Math.round(i * step));
if (min <= value && value <= max) {
result.add(value);
intervalCount++;
}
}
}
return result;
} else {
return super.getXLabels(min, max, count);
}
}
if (mStartPoint == null) {
mStartPoint = min - (min % DAY) + DAY + new Date(Math.round(min)).getTimezoneOffset() * 60
* 1000;
}
if (count > 25) {
count = 25;
}

final double cycleMath = (max - min) / count;
if (cycleMath <= 0) {
return result;
}
double cycle = DAY;

if (cycleMath <= DAY) {
while (cycleMath < cycle / 2) {
cycle = cycle / 2;
}
} else {
while (cycleMath > cycle) {
cycle = cycle * 2;
}
}

double val = mStartPoint - Math.floor((mStartPoint - min) / cycle) * cycle;
int i = 0;
while (val < max && i++ <= count) {
result.add(val);
val += cycle;
}

return result;
}
}

现在就可以在要绘制图表的Activity中使用他们了,下面是OnCreate的一个演示:

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chart);

layout = (LinearLayout) findViewById(R.id.chart_layout);
// 1。 构造显示用渲染图
XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(2);
// 2。 构建数据
XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();

MyTimeSeries amWaight = new MyTimeSeries("起床体重");
MyTimeSeries pmWaight = new MyTimeSeries("就寝体重");
MyTimeSeries step = new MyTimeSeries("步数", 1);

SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
Date thisDay = new Date();
Calendar calendar = new GregorianCalendar();
try {
calendar.setTime(format.parse(format.format(thisDay)));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
double maxValue = 120.0;

for (int i = 0; i < 12; i++) {
amWaight.add(calendar.getTime(), i);
pmWaight.add(calendar.getTime(), i + 1);
step.add(calendar.getTime(), i * 1000);
calendar.add(Calendar.DATE, 1); //若写成calendar.add(Calendar.DATE, -1)会有一个异常,不知道为什么

}

dataset.addSeries(step);
dataset.addSeries(amWaight);
dataset.addSeries(pmWaight);

// 3, 对点的绘制进行设置
XYSeriesRenderer xyRenderer = new XYSeriesRenderer();
// 设置是否在主题上方显示值
xyRenderer.setDisplayChartValues(true);
xyRenderer.setColor(Color.LTGRAY);// 设置柱状图的颜色
renderer.addSeriesRenderer(xyRenderer);

xyRenderer = new XYSeriesRenderer();
// 设置颜色
xyRenderer.setColor(Color.rgb(5, 141, 199));
// 设置点的样式
xyRenderer.setPointStyle(PointStyle.SQUARE);
// 设置实心点
xyRenderer.setFillPoints(true);
// 设置是否在主题上方显示值
xyRenderer.setDisplayChartValues(true);
//  将要绘制的点添加到坐标绘制中
renderer.addSeriesRenderer(xyRenderer);

// 第2个折线
xyRenderer = new XYSeriesRenderer();
xyRenderer.setColor(Color.rgb(5, 199, 100));
xyRenderer.setPointStyle(PointStyle.CIRCLE);
xyRenderer.setFillPoints(true);
xyRenderer.setDisplayChartValuesDistance(100);
xyRenderer.setDisplayChartValues(true);
renderer.addSeriesRenderer(xyRenderer);

// 设置坐标轴,轴的颜色
renderer.setAxesColor(Color.BLACK);
// 显示网格
renderer.setShowGrid(true);
// 设置x,y轴上的刻度的颜色
renderer.setLabelsColor(Color.BLACK);
renderer.setLegendTextSize(15);
renderer.setYLabelsAlign(Align.RIGHT);
renderer.setPanEnabled(true, true);
renderer.setXLabels(15);
renderer.setYLabels(20);
renderer.setBarSpacing(10f);// 柱子间宽度
renderer.setBarWidth(0.5f);
renderer.setYAxisAlign(Align.RIGHT, 1);
renderer.setYLabelsAlign(Align.LEFT, 1);
renderer.setGridColor(Color.DKGRAY);
renderer.setShowGridX(false);
renderer.setPanLimits(new double[] { calendar.getTime().getTime(),
thisDay.getTime(), -1.0, maxValue });
renderer.setBackgroundColor(Color.BLACK);
renderer.setApplyBackgroundColor(true);

String[] types = { BarChart.TYPE, TimeChart.TYPE, TimeChart.TYPE };

mChartView = getCombinedTimeChartView(this, dataset, renderer, types);
if (mChartView != null)
layout.addView(mChartView, new LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

}

在Activity中加入下面两个函数,用于得到图表:

public GraphicalView getCombinedTimeChartView(Context context,
XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer,
String[] types) {
if (dataset == null || renderer == null || types == null
|| dataset.getSeriesCount() != types.length) {
throw new IllegalArgumentException(
"Dataset, renderer and types should be not null and the datasets series count should be equal to the types length");
}
checkParameters(dataset, renderer);
CombinedTimeChart chart = new CombinedTimeChart(dataset, renderer,
types);
chart.setDateFormat("yy/MM/dd");
return new GraphicalView(context, chart);
}

private static void checkParameters(XYMultipleSeriesDataset dataset,
XYMultipleSeriesRenderer renderer) {
if (dataset == null
|| renderer == null
|| dataset.getSeriesCount() != renderer
.getSeriesRendererCount()) {
throw new IllegalArgumentException(
"Dataset and renderer should be not null and should have the same number of series");
}
}


下面是效果图:



我还发现如果逆序添加数据并在图表上显示数据会导致一个越界异常,比如上面添加数据中的
calendar.add(Calendar.DATE, 1);

改成
calendar.add(Calendar.DATE, -1);

并且
xyRenderer.setDisplayChartValues(true);

就会发生错误,还不知道为什么。

例子源码:百度网盘(aChartEngine库是我down的源码自己编译的不是1.1版)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: