2012年9月13日 星期四

如何在Android上繪出折線圖、條狀圖、圓餅圖、...


一般而言,如果要在Android繪出折線圖、條狀圖、圓餅圖、...這些常見的統計圖,在Android APIs中的android.graphics有提供一個Canvas類別來使用。

這個類別好處是Android內建的類別,使用上不會有授權的問題,但缺點是程式撰寫上要多一些程序;例如繪出折線圖要線繪出點然後再繪出點與點的線最後再進行圖表的美化,這些都需要寫出程式碼才能實現折線圖在螢幕上。

不過,今天不講Canvas類的使用,而是介紹一個比較簡單而且很容易就上手的方法--AChartEngine (achartengine.jar)


在Java程式中,有些功能內建的函式庫是無法達成的,這時我們常見使用非官方的函式庫嵌入到程式中來使用,除了使用簡單外就是不用去浪費時間寫出一堆程式碼。
AChartEngine是一個圖表的Android應用函式庫。它目前支持以下圖表類型:
折線圖
面積圖
散點圖
時間圖
條圖
餅圖
氣泡圖
圓環圖
條形圖
撥號圖/表
複合線(可合併折線圖、立方線圖、散點圖、條圖、條形圖、氣泡圖)
立方線圖

總類相當多,有需要的可以到官網下載 achartengine-1.0.0-demo-source.zip ,補充一點是如果使用ICS的話這個DEMO程式要將Project Build Target中的Target Name由2.1改成4.03,這部分只要在用滑鼠右鍵點選專案-->Properties-->Android就可看到了。

下面以常用的折線圖進行一個新的程式實作:
1.用Eclipse開啟一個新的專案命名為Hellochart。


2.至AChartEngine官網下載achartengine-1.0.0.jar後,將其放置在專案的lib目錄中,然後用滑鼠右鍵點選專案-->Refresh就類似下圖中libs內有achartengine-1.0.0.jar畫面。



3.用滑鼠右鍵點選專案-->Properties-->Java Build Path-->Libraries按下Add JATs... -->找到並打開 libs 目錄 -->選擇achartengine-1.0.0.jar --> ok。


4.接著不要離開,繼續選擇上方的Order and Export 見到 achartengine-1.0.0.jar - Hellochart/libs 將其打勾 然後選擇ok離開。

5.開始撰寫MainActivity.java





程式中 28-33行定義了兩條線顯示的數值,
35-37定義並繪出兩條線,
第39行對應到44-61行定義了標題、X,Y軸....等等,相關參數請參閱Class DefaultRenderer
利用上述DefaultRenderer類別的相參數可以繪製屬於自己風格的圖表。
輸出結果:


結論是利用AChartEngine函式庫來畫統計圖與Android Canvas類別省去了約一半以上的程式碼,至於其他如條圖、餅圖、....等等就參考官網所提供的DEMO囉。

參考:
1.achartengine官網
http://code.google.com/p/achartengine/

2.Class DefaultRenderer
http://www.achartengine.org/content/javadoc/org/achartengine/renderer/DefaultRenderer.html#addSeriesRenderer(int, org.achartengine.renderer.SimpleSeriesRenderer)

程式碼:
package edu.nkut.hellochart;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;

import org.achartengine.ChartFactory;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // setContentView(R.layout.activity_main);

        String[] titles = new String[] { "折線1", "折線2" }; // 定義折線的名稱
        List<double[]> x = new ArrayList<double[]>(); // 點的x坐標
        List<double[]> y = new ArrayList<double[]>(); // 點的y坐標
        // 數值X,Y坐標值輸入
        x.add(new double[] { 1, 3, 5, 7, 9, 11 });
        x.add(new double[] { 0, 2, 4, 6, 8, 10 });
        y.add(new double[] { 3, 14, 8, 22, 16, 18 });
        y.add(new double[] { 20, 18, 15, 12, 10, 8 });
        XYMultipleSeriesDataset dataset = buildDatset(titles, x, y); // 儲存座標值

        int[] colors = new int[] { Color.BLUE, Color.GREEN };// 折線的顏色
        PointStyle[] styles = new PointStyle[] { PointStyle.CIRCLE, PointStyle.DIAMOND }; // 折線點的形狀
        XYMultipleSeriesRenderer renderer = buildRenderer(colors, styles, true);

        setChartSettings(renderer, "折線圖展示", "X軸名稱", "Y軸名稱", 0, 12, 0, 25, Color.BLACK);// 定義折線圖
        View chart = ChartFactory.getLineChartView(this, dataset, renderer);
        setContentView(chart);
    }

    // 定義折線圖名稱
    protected void setChartSettings(XYMultipleSeriesRenderer renderer, String title, String xTitle,
            String yTitle, double xMin, double xMax, double yMin, double yMax, int axesColor) {
        renderer.setChartTitle(title); // 折線圖名稱
        renderer.setChartTitleTextSize(24); // 折線圖名稱字形大小
        renderer.setXTitle(xTitle); // X軸名稱
        renderer.setYTitle(yTitle); // Y軸名稱
        renderer.setXAxisMin(xMin); // X軸顯示最小值
        renderer.setXAxisMax(xMax); // X軸顯示最大值
        renderer.setXLabelsColor(Color.BLACK); // X軸線顏色
        renderer.setYAxisMin(yMin); // Y軸顯示最小值
        renderer.setYAxisMax(yMax); // Y軸顯示最大值
        renderer.setAxesColor(axesColor); // 設定坐標軸顏色
        renderer.setYLabelsColor(0, Color.BLACK); // Y軸線顏色
        renderer.setLabelsColor(Color.BLACK); // 設定標籤顏色
        renderer.setMarginsColor(Color.WHITE); // 設定背景顏色
        renderer.setShowGrid(true); // 設定格線
    }

    // 定義折線圖的格式
    private XYMultipleSeriesRenderer buildRenderer(int[] colors, PointStyle[] styles, boolean fill) {
        XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
        int length = colors.length;
        for (int i = 0; i < length; i++) {
            XYSeriesRenderer r = new XYSeriesRenderer();
            r.setColor(colors[i]);
            r.setPointStyle(styles[i]);
            r.setFillPoints(fill);
            renderer.addSeriesRenderer(r); //將座標變成線加入圖中顯示
        }
        return renderer;
    }

    // 資料處理
    private XYMultipleSeriesDataset buildDatset(String[] titles, List<double[]> xValues,
            List<double[]> yValues) {
        XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();

        int length = titles.length; // 折線數量
        for (int i = 0; i < length; i++) {
            // XYseries對象,用於提供繪製的點集合的資料
            XYSeries series = new XYSeries(titles[i]); // 依據每條線的名稱新增
            double[] xV = xValues.get(i); // 獲取第i條線的資料
            double[] yV = yValues.get(i);
            int seriesLength = xV.length; // 有幾個點

            for (int k = 0; k < seriesLength; k++) // 每條線裡有幾個點
            {
                series.add(xV[k], yV[k]);
            }
            dataset.addSeries(series);
        }
        return dataset;
    }
}


==============延伸閱讀=====================
1.如何在Android上繪出類似心跳圖的動態線
http://cheng-min-i-taiwan.blogspot.tw/2013/09/android.html

14 則留言:

  1. 太棒了!!我希望可以參考這篇教學
    來完成 用手機藍芽接收數據 畫出溫度濕度曲線圖!!!

    回覆刪除
    回覆
    1. 喀擦
      不好意思可以請教您一些相關問題嗎???
      不知有何方式可以跟您聯絡

      刪除
  2. TO 蚊子版主:
    我再Order and Export 沒有見到 achartengine-1.0.0.jar - Hellochart/libs 請問該如何解決呢?
    謝謝

    回覆刪除
    回覆
    1. 應該是沒有放到指定的目錄下,2,3,4步驟比對一下檔案所放置的專案目錄是否跟文章一樣。

      刪除
    2. 是的!第三步驟漏了一些步驟
      真的感謝蚊子版主

      刪除
  3. 您好,我使用bar chart
    在Y軸想要顯示負值,從-100(最小值)到-30(最大值)
    當設定barSeries.add(1, -50)後
    發現bar是從上面長下來而不是從-100到-50
    似乎AChartEngine是看絕對值
    請問可以如何調整?謝謝您

    回覆刪除
  4. 不好意思請問一下
    我想把這個折線圖表利用一個按鈕intent到另外一個頁面顯示
    不過我看你上面 // setContentView(R.layout.activity_main);版面配置這行是註解
    我也有嘗試過不用版面配置或是使用版面配置
    但是都是在按下按鈕後程式就當掉了
    可以請問一下有甚麼解決方法嗎?!

    回覆刪除
  5. 您好,請問若想調整折線圖顯示的位置,是調整哪一個變數呢?

    回覆刪除
  6. 真的是簡單明瞭,
    謝謝您!

    回覆刪除
  7. 如果想用資料庫的資料做折線圖,怎樣將資料加入List ?

    回覆刪除
  8. 請問大大 他要怎麼設定出第三條線
    我增加顏色跟折線名稱 但是卻不行
    我減少一個折線名稱跟顏色 折線卻有變少 不知道哪裡出問題

    回覆刪除