MPAndroidChart

參考文件

ref link
github https://github.com/PhilJay/MPAndroidChart

簡介

這是一個功能強大的繪製圖表Library,總計有八種不同的圖表 功能應有盡有,能想的到的幾乎都做得到

如何使用library

1. 使用 jar 檔

點開以下連結下載最新版 jar 檔 https://github.com/PhilJay/MPAndroidChart/releases

將下載好的 jar 檔複製到你的專案底下(Android studio) yourproject/app/libs

確保你的build.gradle有以下程式碼

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
}

或是自行手動加入

2. gradle

在build.gradle 輸入

repositories {
    maven { url "https://jitpack.io" }
}

dependencies {
    compile 'com.github.PhilJay:MPAndroidChart:v2.1.0'
}

繪製圖表

以下皆以折線圖為例

1. 在xml中加入指定的圖表

    <com.github.mikephil.charting.charts.LineChart
        android:id="@+id/chart_line"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

2. 建立資料

每一個資料點就是一個Entry(y, x),其中y的型態是float,x為int。以下範產生出(0,0), (1,2), (2,4), (3,6), (4,8)五個點

    private List<Entry> getChartData(){
        final int DATA_COUNT = 5;

        List<Entry> chartData = new ArrayList<>();
        for(int i=0;i<DATA_COUNT;i++){
            chartData.add(new Entry(i*2, i));
        }
        return chartData;
    }

3. 建立X Label

用來代表一筆資料在X座標位置所相對應的顯示字串,以下產生出'X0', 'X1', 'X2', 'X3', 'X4'

    private List<String> getLabels(){
        List<String> chartLabels = new ArrayList<>();
        for(int i=0;i<DATA_COUNT;i++){
            chartLabels.add("X"+i);
        }
        return chartLabels;
    }

4. 整合圖表資料

一組圖表資料為一個DataSet,一個圖表可以同時顯示多筆DataSets,我們現在只產生一組DataSet,將它整合到圖表資料(LineData)裡

    private LineData getLineData(){
        LineDataSet dataSetA = new LineDataSet(getChartData(), "LabelA");

        List<LineDataSet> dataSets = new ArrayList<>();
        dataSets.add(dataSetA); // add the datasets

        return new LineData(getLabels(), dataSets);
    }

    ......

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LineChart chart_line = (LineChart)findViewById(R.id.chart_line);
        chart_line.setData(getLineData());
    }

5. 顯示畫面

進階版

現在已經可以繪製基本圖形了, 但是離好看的圖表還有一大段距離, 本章節帶各位一步一步刻出好看的圖表。

1. 目標範本

2. 事前準備

在github readme影片中可以看到 裡面有個 Stacked Bar Chart 可以畫出這次範本的圖表。

先看看作者的 readme 通常可以節省很多追code的時間

調查過發現,這種圖表其實是以 BarChart 為基底畫出來的, github的範例在這裡:StackedBarActivity

不知如何下手時,通常都可以在作者提供的 sample 中找到呼叫API的方法

3. 繪製BarChart

大致上與上一份程式碼差不多,就不多加解釋了

    <com.github.mikephil.charting.charts.BarChart
        android:id="@+id/chart_bar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
private int DATA_COUNT = 5;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BarChart chart_bar = (BarChart)findViewById(R.id.chart_bar);
        chart_bar.setData(getBarData());
    }

    private BarData getBarData(){
        BarDataSet dataSetA = new BarDataSet(getChartData(), "LabelA");

        List<BarDataSet> dataSets = new ArrayList<>();
        dataSets.add(dataSetA); // add the datasets

        return new BarData(getLabels(), dataSets);
    }

    private List<BarEntry> getChartData(){
        final int DATA_COUNT = 5;

        List<BarEntry> chartData = new ArrayList<>();
        for(int i=0;i<DATA_COUNT;i++){
            chartData.add(new BarEntry(i*2, i));
        }
        return chartData;
    }

    private List<String> getLabels(){
        List<String> chartLabels = new ArrayList<>();
        for(int i=0;i<DATA_COUNT;i++){
            chartLabels.add("X"+i);
        }
        return chartLabels;
    }

3. 改造成Stacked Bar Chart

依據作者提供的範例,我們知道了只要將顯示的資料以 float array 的資料結構放進BarEntry即可(StackedBarActivity 198-209)

    private BarData getBarData(){
        BarDataSet dataSetA = new BarDataSet(getChartData(), getString(R.string.chart_title));
        //設定顏色
        dataSetA.setColors(getChartColors());
        //設定顯示字串
        dataSetA.setStackLabels(getStackLabels());

        List<BarDataSet> dataSets = new ArrayList<>();
        dataSets.add(dataSetA); // add the datasets

        return new BarData(getLabels(), dataSets);
    }

    private String[] getStackLabels(){
        return new String[]{getString(R.string.chart_label_Others),
                getString(R.string.chart_label_KR),
                getString(R.string.chart_label_JP),
                getString(R.string.chart_label_US)};
    }

    private int[] getChartColors() {
        int[] colors = new int[]{getResourceColor(R.color.chart_color_Others),
                getResourceColor(R.color.chart_color_KR),
                getResourceColor(R.color.chart_color_JP)
                ,getResourceColor(R.color.chart_color_US) };
        return colors;
    }

    private int getResourceColor(int resID){
        return getResources().getColor(resID);
    }

    private List<BarEntry> getChartData(){
        final int DATA_COUNT = 5;

        List<BarEntry> chartData = new ArrayList<>();
        //每一個月都有四筆資料
        for(int i=0;i<DATA_COUNT;i++){
            float revenue_US = i*5;
            float revenue_JP = i*2;
            float revenue_KR = i*2;
            float revenue_Other = i*4;
            chartData.add(new BarEntry(new float[]{revenue_Other, revenue_KR, revenue_JP, revenue_US}, i));
        }
        return chartData;
    }

附上色碼,字串就不提供了

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="chart_color_US">#576F86</color>
    <color name="chart_color_JP">#E7655A</color>
    <color name="chart_color_KR">#F6CE5D</color>
    <color name="chart_color_Others">#BAE6FB</color>
</resources>

3. 將不必要的資訊拿掉

跟原圖比較之後,可以觀察到這幾點:

  1. 多了右下角的"Description"
  2. 需要把背景格線去掉
  3. 資料上方數值不該顯示
  4. X軸 Label 應該顯示在下方

*先從最簡單的開始,Description: Chart 有一個 public method : setDescription(String desc),只要設空字串即可

    chart_bar.setDescription("");

*去掉背景格線:

這個就比較麻煩了,在之前的版本中,可以直接在BarChart呼叫setDrawHorizontalGrid(false), 跟setDrawVerticalGrid(false)來隱藏格線,作者不知道在甚麼時候移除了。

更新Library時要注意是否有API已經被修改了,正常來說不應該發生,但是這種opensource的專案有可能說改就改,在使用上要小心。

於是我又再找了一下範例,發現作者把畫格線的責任交給了 XAxis, YAxis 這兩個物件, 於是就針對這些物件來做設定

    private void configChartAxis(BarChart chart_bar){
        XAxis xAxis = chart_bar.getXAxis();
        xAxis.setDrawGridLines(false);

        YAxis leftYAxis = chart_bar.getAxisLeft();
        leftYAxis.setDrawGridLines(false);

        YAxis RightYAxis = chart_bar.getAxisRight();
        RightYAxis.setEnabled(false);   //不顯示右側
    }

*隱藏資料上方數值: 資料數值顯示與否與資料有關,所以相關設定在BarData裡

    BarData barData = getBarData();
    barData.setDrawValues(false);

*x軸 Lable 顯示在下方:

這個也不是馬上就能找到的。首先,有了剛剛的經驗,第一個直覺是:這應該一樣是由 XAxis這物件去控制的。但是這次走點不一樣的路,從 source code 下手!

這邊假設讀者對 View 有一定程度的認識,於是這邊就直接看source code的 onDraw()而不說原因了,以下程式碼截取自 [BarLineChartBase] (https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java)

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mDataNotSet)
            return;

        long starttime = System.currentTimeMillis();
        calcModulus();

        mXAxisRenderer.calcXBounds(this, mXAxis.mAxisLabelModulus);
        mRenderer.calcXBounds(this, mXAxis.mAxisLabelModulus);

        // execute all drawing commands
        drawGridBackground(canvas);

        if (mAxisLeft.isEnabled())
            mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum);
        if (mAxisRight.isEnabled())
            mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum);

        mXAxisRenderer.renderAxisLine(canvas);
        mAxisRendererLeft.renderAxisLine(canvas);
        mAxisRendererRight.renderAxisLine(canvas);

        ........

上面中的 mXAxisRenderer.renderAxisLine(canvas) 名稱看起來就是在畫x軸,接下來就點進去看他的實作細節來驗證(XAxisRenderer)

@Override
    public void renderAxisLabels(Canvas c) {

        if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled())
            return;

        float yoffset = Utils.convertDpToPixel(4f);

        mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
        mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
        mAxisLabelPaint.setColor(mXAxis.getTextColor());

        if (mXAxis.getPosition() == XAxisPosition.TOP) {

            drawLabels(c, mViewPortHandler.offsetTop() - yoffset);

        } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) {

            drawLabels(c, mViewPortHandler.contentBottom() + mXAxis.mLabelHeight + yoffset * 1.5f);

        } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) {

            drawLabels(c, mViewPortHandler.contentBottom() - yoffset);

        } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) {

            drawLabels(c, mViewPortHandler.offsetTop() + yoffset + mXAxis.mLabelHeight);

        } else { // BOTH SIDED

            drawLabels(c, mViewPortHandler.offsetTop() - yoffset);
            drawLabels(c, mViewPortHandler.contentBottom() + mXAxis.mLabelHeight + yoffset * 1.6f);
        }
    }

由這段程式碼應該不難看出來,顯示 Label 的位置是由 mXAxis.getPosition() 來決定,於是就知道該如何設定顯示 label 位置

    private void configChartAxis(BarChart chart_bar){
        XAxis xAxis = chart_bar.getXAxis();
        xAxis.setDrawGridLines(false);
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);

        ...
    }

本次階段結果:

results matching ""

    No results matching ""