2011年3月20日日曜日

Android AFreeChartで円グラフを描く

小遣い帳アプリで使ったAFreeChartで円グラフを描いたので、それについてまとめる。自分用のまとめなので間違いを含んでるかもしれないがとりあえずまとめる。まとめるというかメモ。
AFreeChartJFreeChartをAndroid用に拡張したライブラリ。ライセンスはLGPL。

ググっても日本語ではサンプルも見つけられなかったので、ソースを見てみる。ちゃんと円グラフのPieChartのデモがありますね。PieChartDemo1ActivityPieChartDemo1Viewがサンプル。ActivityはViewをnewしてsetしてるだけで、ViewもサンプルのDatasetでchartを作ってsetChartしてるだけ。Viewっていうとイメージ的にはデータ作ったりしないけど、このDemoではViewがDatasetを作ってる。ま、そんなことはいいとして、DemoViewもみないとな。見てみると、ちょっと自分のレベルを越えていて、全てを理解はできない。

まぁ、グラフ描画に必要なのはなんとなく分かった。AFreeChartを持ったViewを用意して、onDrawをオーバーライドして、AFreeChartにdrawさせれば良いんだろう。
public class SamplePieChartView extends View {
    private AFreeChart chart;

    public SamplePieChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        RectShape chartArea = new RectShape(0.0, 0.0, 200.0, 200.0);
        this.chart.draw(canvas, chartArea);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(200, 200);
    }

    public void setChart(AFreeChart chart) {
        this.chart = chart;
    }
}
と、言うことでシンプルなViewを用意。DemoViewで理解のできなかった幅などの計算はしないで、200×200という決め打ちにして。

ActivityではDatasetを作って、setChartする。
public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        DefaultPieDataset dataset = new DefaultPieDataset();
        dataset.setValue("Cupcake", new Integer(40));
        dataset.setValue("Donut", new Integer(5));
        dataset.setValue("Eclair", new Integer(10));
        dataset.setValue("Froyo", new Integer(25));
        dataset.setValue("Gingerbread", new Integer(20));
        dataset.setValue("Honeycomb", new Integer(50));
        AFreeChart chart = ChartFactory.createPieChart("サンプル", dataset,
                                                       true, false, false);
        PiePlot plot = (PiePlot) chart.getPlot();
        plot.setLabelFont(new Font("SansSerif", Typeface.NORMAL, 8));
//        plot.setCircular(false);            // false=楕円
//        plot.setLabelLinksVisible(false);   // false=link(Label-Pie)なし

        SamplePieChartView spcv = (SamplePieChartView) findViewById(R.id.spcv);
        spcv.setChart(chart);
    }
}
main.xmlは下記の通り。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:text="SamplePieChartView"
              android:layout_margin="10dip"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content" />
    <com.sample.afreechart.SamplePieChartView
              android:id="@+id/spcv"
              android:layout_margin="10dip"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content" />
</LinearLayout>
結果はこんな感じで円グラフを表示できる。
円グラフの周りにあるラベルが邪魔くさく消したかったので、ソース見てみたが無理そうであった。setLabelLinksVisibleでラベルと円グラフの間の線を消せるくらいで、ラベル自体は消せないよう。しっかり見きれてないので、もしかしたら消せるのかも。

とりあえず、こんな感じでViewを独自に作ってやって小遣い帳アプリには組み込んでやった。とてもアプリのソースはそのまま載せられないので、この記事用にシンプルなサンプルを書いてみた。
で、グラフの表示自体はできたけど200×200ってのがやっぱり汎用性に欠けるなと思いつつ、DemoViewを再度眺める。やっぱりまだ理解には及ばないな。。でも、このDemoView名前にDemoとついてるせいか使うことを思いつかなかったが、これそのまま使えるんじゃない?

ということで、main.xmlに下記追記。
    <TextView android:text="DemoView"
              android:layout_margin="10dip"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content" />
    <org.afree.chart.demo.DemoView
              android:id="@+id/dv"
              android:layout_margin="10dip"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content" />
MainActivityに下記追記。
        DemoView dv = (DemoView) findViewById(R.id.dv);
        dv.setChart(chart);
実行。
表示できますね。サイズを指定していないので、画面幅いっぱいに広がっているが、setMaximumDrawWidthやsetMaximumDrawHeightなどで、サイズも指定できる。内部のソースを全て理解はできなくても、利用はできるということか。

ただ、DemoView使う場合にsetChartを必ずしないといけない。しないとonDraw関数から呼ばれるpaintComponentでchartが使われるので、NullPointerExceptionになってしまう。グラフ表示したりしなかったりということもなく、必ずsetChartするということで気にすることでもないのかな。

てなわけで、独自にView作ってやるよりもDemoViewをそのまま使ってやった方がいいのかな。と、思う。

※追記
AChartEngineで円グラフを描いてみたが、achartengineではafreechartで消せなかった円グラフの周りのラベルを消せるよう。