AChartEngine xy date plot example

Today I decided to include in my application some chart. At the beginning I was trying to achieve this by using Androidplot, but after few hours I switched to AChartEngine. I was unable to find how to show point value labels. With AChartEngine it was easier.

Problem with AChartEngine is that they have no documentation. You can only download example application and look through the code to see how to display some charts. I don’t like this, because in those examples you cannot clearly see how to use this library. So I decided to publish some simple example with my code.

So first lets create some data class.

public class Result implements Comparable<Result>{

	private Date date;
	private Double value;

	public Result(Date date, Double value) {
		this.date = date;
		this.value = value;
	}

	public Date getDate() {
		return date;
	}

	public Double getValue() {
		return value;
	}

	@Override
	public int compareTo(Result rs) {
		return value.compareTo(rs.getValue());
	}
}

Than lets create Activity which will contain chart.

public class ChartActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		FuelChart chart = new FuelChart();
		setContentView(chart.getView(this, getResults()));
	}

	private List<Result> getResults() {
		List<Result> results = new ArrayList<Result>();
		results.add(new Result(new Date(108, 9, 1), 8.8));
		results.add(new Result(new Date(108, 9, 8), 9.0));
		results.add(new Result(new Date(108, 9, 15), 10.0));
		results.add(new Result(new Date(108, 9, 22), 9.5));
		return results;
	}

Then map it in AndroidManifest.xml

<activity
		android:name=".ChartActivity"
		android:label="@string/Chart">
</activity>

And now the most important part. Let’s do some charting.

public class FuelChart {

	private static final String DATE_FORMAT = "dd/MM/yyyy";

	public GraphicalView getView(Context context, List<Result> results) {
		String title = context.getString(R.string.chartTitle);

		int[] colors = new int[] { Color.GREEN };
		PointStyle[] styles = new PointStyle[] { PointStyle.POINT};
		XYMultipleSeriesRenderer renderer = buildRenderer(colors, styles);

		Result max = Collections.max(results);
		Result min = Collections.min(results);

		setChartSettings(renderer, context.getString(R.string.chartTitle),
				context.getString(R.string.date),
				context.getString(R.string.value), results.get(0)
						.getDate().getTime(), results.get(results.size() - 1)
						.getDate().getTime(), min.getValue(), max.getValue(), Color.GRAY, Color.LTGRAY);
		renderer.setXLabels(5);
		renderer.setYLabels(10);
		SimpleSeriesRenderer seriesRenderer = renderer.getSeriesRendererAt(0);
		seriesRenderer.setDisplayChartValues(true);

		return ChartFactory.getTimeChartView(context,
				buildDateDataset(title, results), renderer, DATE_FORMAT);
	}

	protected void setChartSettings(XYMultipleSeriesRenderer renderer,
			String title, String xTitle, String yTitle, double xMin,
			double xMax, double yMin, double yMax, int axesColor,
			int labelsColor) {
		renderer.setChartTitle(title);
		renderer.setXTitle(xTitle);
		renderer.setYTitle(yTitle);
		renderer.setXAxisMin(xMin);
		renderer.setXAxisMax(xMax);
		renderer.setYAxisMin(yMin);
		renderer.setYAxisMax(yMax);
		renderer.setAxesColor(axesColor);
		renderer.setLabelsColor(labelsColor);
	}

	protected XYMultipleSeriesRenderer buildRenderer(int[] colors,
			PointStyle[] styles) {
		XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
		setRendererProperties(renderer, colors, styles);
		return renderer;
	}

	protected XYMultipleSeriesDataset buildDateDataset(String title,
			List<Result> results) {
		XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
		TimeSeries series = new TimeSeries(title);
		for (Result result : results) {
			series.add(result.getDate(), result.getValue());
		}
		dataset.addSeries(series);
		return dataset;
	}

	protected void setRendererProperties(XYMultipleSeriesRenderer renderer, int[] colors,
			PointStyle[] styles) {
		renderer.setAxisTitleTextSize(16);
		renderer.setChartTitleTextSize(20);
		renderer.setLabelsTextSize(15);
		renderer.setLegendTextSize(15);
		renderer.setPointSize(5f);
		renderer.setMargins(new int[] { 20, 30, 15, 20 });
		int length = colors.length;
		for (int i = 0; i < length; i++) {
			XYSeriesRenderer r = new XYSeriesRenderer();
			r.setColor(colors[i]);
			r.setPointStyle(styles[i]);
			renderer.addSeriesRenderer(r);
		}
	}
}

As you can see from this example most important method is

	ChartFactory.getTimeChartView(context, dateDataset, renderer, DATE_FORMAT);

This method will provide GraphicalView which we can use in Activity. ChartFactory has more similiar methods for each type of supported chart.

  • Fab

    Nice. Was looking for that. I am trying to add data dynamically and have the graph refreshed but does not manage yet… any idea ? I am trying to invalidate the view but nothing happens.
    Thanks.

    • http://coffeedrivendevelopment.wordpress.com Maciej Dzikowicki

      Hi
      I didn’t try to refresh that chart, but I will play with it and let you know.

      Maciek

  • Niels

    This helps alot, but I’m encountering an error at the setContentView. It simply crashes. :(

    • http://coffeedrivendevelopment.wordpress.com Maciej Dzikowicki

      What error exactly do you get?

  • Niels

    Figured out it was an update error in sdk r18 and r17:

    http://android.foxykeep.com/dev/how-to-fix-the-classdefnotfounderror-with-adt-17

    • babu

      thanks Sir, I had the same problem, wondering whether it could be caz of android or caz of achartengine. You made things easy.

  • Niels

    I’m trying to convert the code to make a scatterplot, but I’m not clear on how to add multiple series.

  • andrea

    I’ve a problem with this code’s part:

    SimpleSeriesRenderer seriesRenderer = renderer.getSeriesRendererAt(0);
    seriesRenderer.setDisplayChartValues(true);

    If I comment those lines all works well but I can’t display values on series….
    What is the problem?

    These are my errors messages:

    07-24 16:48:54.250: E/AndroidRuntime(20154): FATAL EXCEPTION: main
    07-24 16:48:54.250: E/AndroidRuntime(20154): java.lang.IndexOutOfBoundsException: Invalid index 61, size is 43
    07-24 16:48:54.250: E/AndroidRuntime(20154): at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:257)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at java.util.ArrayList.get(ArrayList.java:311)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at org.achartengine.util.IndexXYMap.getYByIndex(IndexXYMap.java:80)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at org.achartengine.model.XYSeries.getY(XYSeries.java:169)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at org.achartengine.chart.XYChart.drawChartValuesText(XYChart.java:551)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at org.achartengine.chart.XYChart.drawSeries(XYChart.java:502)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at org.achartengine.chart.XYChart.draw(XYChart.java:286)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at org.achartengine.GraphicalView.onDraw(GraphicalView.java:166)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.View.draw(View.java:6880)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.View.draw(View.java:6883)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.widget.FrameLayout.draw(FrameLayout.java:357)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.ViewGroup.drawChild(ViewGroup.java:1644)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.View.draw(View.java:6883)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.widget.FrameLayout.draw(FrameLayout.java:357)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1940)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.ViewRoot.draw(ViewRoot.java:1527)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.ViewRoot.performTraversals(ViewRoot.java:1264)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.view.ViewRoot.handleMessage(ViewRoot.java:1865)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.os.Handler.dispatchMessage(Handler.java:99)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.os.Looper.loop(Looper.java:130)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at android.app.ActivityThread.main(ActivityThread.java:3687)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at java.lang.reflect.Method.invokeNative(Native Method)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at java.lang.reflect.Method.invoke(Method.java:507)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
    07-24 16:48:54.250: E/AndroidRuntime(20154): at dalvik.system.NativeStart.main(Native Method)