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. 將不必要的資訊拿掉
跟原圖比較之後,可以觀察到這幾點:
- 多了右下角的"Description"
- 需要把背景格線去掉
- 資料上方數值不該顯示
- 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);
...
}
本次階段結果: