Wednesday, September 10, 2014

Formatting charts on Android with AChartEngine

AChartEngine is a great tool to allow you to quickly add charts of various types to your Android app.
As good as it is, it's unfortunately not perfect.  I'm currently running into some formatting problems when using larger font sizes.  I'll be opening a bug ticket shortly, but this blog entry will allow me to go into detail more easily than I could in the bug reporting system.

At the end of this post is source code.  Using that code, you get the following result when run on a phone:



You can see that the legend is crowding the bottom of the graph to the point of overlapping the X axis title.  This problem becomes more pronounced as font size increases, and is much worse on a tablet, where the code increases the font size even further.

I searched around in the AChartEngine source a bit, in the hopes of generating a patch that just fixed the problem.  I feel that the problematic code is line 104 of AbstractChart.java:

float currentY = y + height - legendSize + size;

Searching around the code, I couldn't come to an understanding of what that simple equation is supposed to be doing.  Instead, I took a brute-force approach and replaced it with:

float currentY = height - size;

Which unclutters the result:



While this works for me, I'm fairly sure that it's not correct overall.  In particular, I don't expect it to work when there are many series that cause the legend to span multiple lines.

Hopefully there's enough in this post to make it easy for someone else to generate a correct fix.

Source code:

// imports omitted for clarity
public class MyActivity extends ActionBarActivity {

  private final static Random rand = new Random();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_my);
  }

  @Override
  protected void onResume() {
    super.onResume();
    Point p = new Point();
    p.x = getWindowManager().getDefaultDisplay().getWidth();
    p.y = getWindowManager().getDefaultDisplay().getHeight();
    int maxDim = p.x > p.y ? p.x : p.y;
    int fontSize = maxDim / 60;
    LinearLayout layout = (LinearLayout) findViewById(R.id.graph_surface);
    XYSeries series1 = new XYSeries("Series 1");
    setXYSeriesData(series1);
    XYSeries series2 = new XYSeries("Series 2");
    setXYSeriesData(series2);
    XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
    renderer.setLabelsTextSize(fontSize);
    renderer.setLegendTextSize(fontSize * 2);
    renderer.setShowLabels(true);
    renderer.setAxisTitleTextSize(fontSize);
    XYSeriesRenderer r = new XYSeriesRenderer();
    r.setColor(Color.RED);
    renderer.addSeriesRenderer(r);
    r = new XYSeriesRenderer();
    r.setColor(Color.RED);
    renderer.addSeriesRenderer(r);
    renderer.setXTitle("X Title");
    renderer.setYTitle("Y Title");
    XYMultipleSeriesDataset dataSet = new XYMultipleSeriesDataset();
    dataSet.addSeries(series1);
    dataSet.addSeries(series2);
    renderer.setYLabelsAngle(270);
    layout.addView(ChartFactory.getLineChartView(this, dataSet, renderer));
  }

  private void setXYSeriesData(XYSeries r) {
    int start = 1;
    int end = 10;
    int value = 0;
    for (int i = start; i < end; i++) {
      r.add(i, value);
      int change = rand.nextInt(15) - 7;
      value += change;
    }
  }
}

No comments:

Post a Comment