前言
圖表繪製可能是我們專案開發過程中比較常見的需求,簡單點兒的需求,我們通過自定義控制元件就能完成,但是像那種比較複雜的圖表,通過自定義的方式實現起來就比較麻煩了,這個時候,我們就需要藉助第三方的開源庫來實現。
Android 平臺繪圖的開源庫有好幾個,最成熟最出名的就屬於***MPAndroidChart***了,他能幫我們實現曲線圖、折線圖、柱狀圖、餅狀圖,分佈圖等等。同時還可以實現混合圖表。
-
曲線圖
-
下方填充曲線圖,可以設定純色和漸變色
-
混合圖表
-
柱狀圖
如上所示,實現的圖表種類非常多,還沒有列舉完全,功能非常強大,正好最近在專案中有使用到MPAndroidChart,在此做一個總結。
專案中使用效果截圖如下:
基礎設定相關API
-
- Chart 的基礎設定
// 設定是否繪製背景
mChart.setDrawGridBackground(false);
// 設定是否繪製邊框
mChart.setDrawBorders(false);
// 設定是否可以縮放圖表
mChart.setScaleEnabled(true);
// 設定是否可以用手指移動圖表
mChart.setDragEnabled(true);
複製程式碼
注意:這裡說一下後面2個屬性
setScaleEnabled
和setDragEnabled
,設定圖表是佛可以縮放和移動,當手機螢幕一屏顯示不下時,我們希望能通過縮放或者滑動圖表。但是經過試驗,僅僅設定這兩個屬性是不行的,還需要配合Matrix來實現,程式碼如下:
Matrix matrix = new Matrix();
// x軸放大4倍,y不變
matrix.postScale(4.0f, 1.0f);
// 設定縮放
mChart.getViewPortHandler().refresh(matrix, mChart, false);
複製程式碼
- 2,圖表描述相關設定
// 不顯示描述資料
mChart.getDescription().setEnabled(true);
mChart.getAxisRight().setEnabled(false);
// 設定描述
mChart.getDescription().setText("text desc");
// 設定描述顯示的位置,預設是顯示在圖表的右下角的
mChart.getDescription().setPosition(200,100);
複製程式碼
- 3,是否顯示右側y軸
mChart.getAxisRight().setEnabled(false);
複製程式碼
- 4, 圖例相關設定
Legend legend = mChart.getLegend();
//是否顯示
legend.setEnabled(true);
//圖例樣式:有圓點,正方形,短線 幾種樣式
legend.setForm(Legend.LegendForm.CIRCLE);
// 圖例顯示的位置:如下2行程式碼設定圖例顯示在左下角
legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
// 圖例的排列方式:水平排列和豎直排列2種
legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);
// 圖例距離x軸的距離
legend.setXEntrySpace(10f);
//圖例距離y軸的距離
legend.setYEntrySpace(10f);
//圖例的大小
legend.setFormSize(7f);
// 圖例描述文字大小
legend.setTextSize(10);
複製程式碼
- 5,x軸先關設定
XAxis xAxis = mChart.getXAxis();
// 是否顯示x軸線
xAxis.setDrawAxisLine(true);
// 設定x軸線的顏色
xAxis.setAxisLineColor(Color.parseColor("#4cffffff"));
// 是否繪製x方向網格線
xAxis.setDrawGridLines(false);
//x方向網格線的顏色
xAxis.setGridColor(Color.parseColor("#30FFFFFF"));
// 設定x軸資料的位置
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
// 設定x軸文字的大小
xAxis.setTextSize(12);
// 設定x軸資料偏移量
xAxis.setYOffset(5);
final List<String> labels = mLabels;
// 顯示x軸標籤
IAxisValueFormatter formatter = new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
int index = (int) value;
if (index < 0 || index >= labels.size()) {
return "";
}
return labels.get(index);
// return labels.get(Math.min(Math.max((int) value, 0), labels.size() - 1));
}
};
// 引用標籤
xAxis.setValueFormatter(formatter);
// 設定x軸文字顏色
xAxis.setTextColor(mChart.getResources().getColor(R.color.char_text_color));
// 設定x軸每最小刻度 interval
xAxis.setGranularity(1f);
複製程式碼
6,y軸先關設定
YAxis yAxis = mChart.getAxisLeft();
//設定x軸的最大值
yAxis.setAxisMaximum(yMax);
// 設定y軸的最大值
yAxis.setAxisMinimum(yMin);
// 不顯示y軸
yAxis.setDrawAxisLine(false);
// 設定y軸資料的位置
yAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
// 不從y軸發出橫向直線
yAxis.setDrawGridLines(false);
// 是否顯示y軸座標線
yAxis.setDrawZeroLine(true);
// 設定y軸的文字顏色
yAxis.setTextColor(mChart.getResources().getColor(R.color.char_text_color));
// 設定y軸文字的大小
yAxis.setTextSize(12);
// 設定y軸資料偏移量
//yAxis.setXOffset(30);
// yAxis.setYOffset(-3);
yAxis.setXOffset(15);
// 設定y軸label 數量
yAxis.setLabelCount(5, false);
// 設定y軸的最小刻度
yAxis.setGranularity(5);//interval
複製程式碼
- 7,縮放和動畫設定
Matrix matrix = new Matrix();
// 根據資料量來確定 x軸縮放大倍
if (mLabels.size() <= 10) {
matrix.postScale(1.0f, 1.0f);
} else if (mLabels.size() <= 15) {
matrix.postScale(1.5f, 1.0f);
} else if (mLabels.size() <= 20) {
matrix.postScale(2.0f, 1.0f);
} else {
matrix.postScale(3.0f, 1.0f);
}
// 在圖表動畫顯示之前進行縮放
mChart.getViewPortHandler().refresh(matrix, mChart, false);
// x軸執行動畫
mChart.animateX(500);
複製程式碼
- 8, 無資料時,顯示文案
/**
* 顯示無資料的提示
*
* @param mChart
*/
public static void NotShowNoDataText(Chart mChart) {
mChart.clear();
mChart.notifyDataSetChanged();
mChart.setNoDataText("你還沒有記錄資料");
mChart.setNoDataTextColor(Color.WHITE);
// 記得最後要重新整理一下
mChart.invalidate();
}
複製程式碼
- 9,設定marker (1)首先需要繼承MarkerView 實現一個自定義View
public class ChartMarkerView extends MarkerView {
private TextView textView;
/**
* Constructor. Sets up the MarkerView with a custom layout resource.
*
* @param context
* @param layoutResource the layout resource to use for the MarkerView
*/
public ChartMarkerView(Context context, int layoutResource) {
super(context, layoutResource);
textView = (TextView) findViewById(R.id.marker_view_text);
}
@Override
public void refreshContent(Entry e, Highlight highlight) {
float value = e.getY();
textView.setText(DecimalFormatUtils.getIntOrFloatRemainOne(value));
}
@Override
public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
return new MPPointF(-getWidth() / 2f, -getHeight() - 10);
}
}
複製程式碼
(2), 通過addMaker
新增到圖上
combinedChart.setMarker(new ChartMarkerView(context, R.layout.chart_markerview_layout));
複製程式碼
以上就是對Chart 的一些基礎設定,包括x,y軸改如何顯示(顏色、大小)、圖例、描述等等。
下面就看看如何來繪製曲線圖呢?
資料設定
當我們要繪製曲線圖、折線圖、和柱狀圖等等的時候,怎樣將我們要繪製的一個個資料點顯示在圖上的呢?MPAndroidChart 是用dataSet來表示的,我們需要將資料點包裝成DataSet,然後設定到Chart,重新整理繪製就ok了。MPAndroidChart 庫中有很多DataSet,如下所示:
以曲線圖或者折線圖為例,我們使用LineDataSet:
/**
* 獲取LineDataSet
*
* @param entries
* @param label
* @param textColor
* @param lineColor
* @return
*/
public static LineDataSet getLineData(List<Entry> entries, String label, @ColorInt int textColor, @ColorInt int lineColor, boolean isFill) {
LineDataSet dataSet = new LineDataSet(entries, label);
// 設定曲線的顏色
dataSet.setColor(lineColor);
//數值文字顏色
dataSet.setValueTextColor(textColor);
// 模式為貝塞爾曲線
dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
// 是否繪製資料值
dataSet.setDrawValues(false);
// 是否繪製圓點
dataSet.setDrawCircles(true);
dataSet.setDrawCircleHole(false);
// 這裡有一個坑,當我們想隱藏掉高亮線的時候,MarkerView 跟著不見了
// 因此只有將它設定成透明色
dataSet.setHighlightEnabled(true);// 隱藏點選時候的高亮線
//設定高亮線為透明色
dataSet.setHighLightColor(Color.TRANSPARENT);
if (isFill) {
//是否設定填充曲線到x軸之間的區域
dataSet.setDrawFilled(true);
// 填充顏色
dataSet.setFillColor(lineColor);
}
//設定圓點的顏色
dataSet.setCircleColor(lineColor);
// 設定圓點半徑
dataSet.setCircleRadius(3.5f);
// 設定線的寬度
dataSet.setLineWidth(1f);
return dataSet;
}
複製程式碼
將資料繪製到圖表上
如果是一條曲線,直接用LineChart 和LineData就好,如果有多條曲線,就需要用CombinedChart 和 CombinedData (混合圖、混合資料)。可以是LineData 和BarData 混合來繪製曲線和柱狀的混合圖。
/**
* 初始化資料
*
* @param chart
* @param lineDatas
*/
public static void initData(CombinedChart chart, LineData... lineDatas) {
CombinedData combinedData = new CombinedData();
for (LineData lineData : lineDatas) {
combinedData.setData(lineData);
}
chart.setData(combinedData);
chart.invalidate();
}
複製程式碼
最後呼叫invalidate 繪製。
柱狀圖
柱狀圖跟曲線圖其實是一樣的,圖表基礎配置一樣,然後將資料點包裝成BarData,最後包裝成BarDataSet。
/**
* 初始化柱狀圖圖表資料
* @param chart
* @param entries
* @param title
* @param barColor
*/
public static void initBarChart(BarChart chart, List<BarEntry> entries, String title, @ColorInt int barColor) {
BarDataSet set1 = new BarDataSet(entries, title);
set1.setValueTextColor(Color.WHITE);
set1.setColor(barColor);
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(set1);
BarData data = new BarData(dataSets);
data.setValueTextSize(10f);
// 設定bar的寬度,但是點很多少的時候好像沒作用,會拉得很寬
data.setBarWidth(0.1f);
// 設定value值 顏色
data.setValueTextColor(Color.WHITE);
//設定y軸顯示的標籤
data.setValueFormatter(new IValueFormatter() {
@Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
return ((int) (value * 100)) + "%";
}
});
chart.setData(data);
chart.invalidate();
}
複製程式碼
工具類
專案中有很多出使用曲線圖和柱狀圖的地方,因此抽取了一個工具類,完整程式碼如下:
public class MPChartUtils {
/**
* 不顯示無資料的提示
*
* @param mChart
*/
public static void NotShowNoDataText(Chart mChart) {
mChart.clear();
mChart.notifyDataSetChanged();
mChart.setNoDataText("你還沒有記錄資料");
mChart.setNoDataTextColor(Color.WHITE);
mChart.invalidate();
}
/**
* 配置Chart 基礎設定
* @param mChart 圖表
* @param mLabels x 軸標籤
* @param yMax y 軸最大值
* @param yMin y 軸最小值
* @param isShowLegend 是否顯示圖例
*/
public static void configChart(CombinedChart mChart, List<String> mLabels, float yMax, float yMin, boolean isShowLegend) {
mChart.setDrawGridBackground(false);
mChart.setDrawBorders(false);
mChart.setScaleEnabled(false);
mChart.setDragEnabled(true);
mChart.setNoDataText("");
// 不顯示描述資料
mChart.getDescription().setEnabled(false);
mChart.getAxisRight().setEnabled(false);
Legend legend = mChart.getLegend();
// 是否顯示圖例
if (isShowLegend) {
legend.setEnabled(true);
legend.setTextColor(Color.WHITE);
legend.setForm(Legend.LegendForm.CIRCLE);
legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);
legend.setYEntrySpace(20f);
//圖例的大小
legend.setFormSize(7f);
// 圖例描述文字大小
legend.setTextSize(10);
legend.setXEntrySpace(20f);
} else {
legend.setEnabled(false);
}
XAxis xAxis = mChart.getXAxis();
// 是否顯示x軸線
xAxis.setDrawAxisLine(true);
// 設定x軸線的顏色
xAxis.setAxisLineColor(Color.parseColor("#4cffffff"));
// 是否繪製x方向網格線
xAxis.setDrawGridLines(false);
//x方向網格線的顏色
xAxis.setGridColor(Color.parseColor("#30FFFFFF"));
// 設定x軸資料的位置
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
// 設定x軸文字的大小
xAxis.setTextSize(12);
// 設定x軸資料偏移量
xAxis.setYOffset(5);
final List<String> labels = mLabels;
// 顯示x軸標籤
IAxisValueFormatter formatter = new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
int index = (int) value;
if (index < 0 || index >= labels.size()) {
return "";
}
return labels.get(index);
// return labels.get(Math.min(Math.max((int) value, 0), labels.size() - 1));
}
};
// 引用標籤
xAxis.setValueFormatter(formatter);
// 設定x軸文字顏色
xAxis.setTextColor(mChart.getResources().getColor(R.color.char_text_color));
// 設定x軸每最小刻度 interval
xAxis.setGranularity(1f);
YAxis yAxis = mChart.getAxisLeft();
//設定x軸的最大值
yAxis.setAxisMaximum(yMax);
// 設定y軸的最大值
yAxis.setAxisMinimum(yMin);
// 不顯示y軸
yAxis.setDrawAxisLine(false);
// 設定y軸資料的位置
yAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
// 不從y軸發出橫向直線
yAxis.setDrawGridLines(false);
// 是否顯示y軸座標線
yAxis.setDrawZeroLine(true);
// 設定y軸的文字顏色
yAxis.setTextColor(mChart.getResources().getColor(R.color.char_text_color));
// 設定y軸文字的大小
yAxis.setTextSize(12);
// 設定y軸資料偏移量
//yAxis.setXOffset(30);
// yAxis.setYOffset(-3);
yAxis.setXOffset(15);
// yAxis.setGranularity(yGranularity);
yAxis.setLabelCount(5, false);
//yAxis.setGranularity(5);//interval
Matrix matrix = new Matrix();
// 根據資料量來確定 x軸縮放大倍
if (mLabels.size() <= 10) {
matrix.postScale(1.0f, 1.0f);
} else if (mLabels.size() <= 15) {
matrix.postScale(1.5f, 1.0f);
} else if (mLabels.size() <= 20) {
matrix.postScale(2.0f, 1.0f);
} else {
matrix.postScale(3.0f, 1.0f);
}
// 在圖表動畫顯示之前進行縮放
mChart.getViewPortHandler().refresh(matrix, mChart, false);
// x軸執行動畫
mChart.animateX(500);
}
/**
* 初始化資料
*
* @param chart
* @param lineDatas
*/
public static void initData(CombinedChart chart, LineData... lineDatas) {
CombinedData combinedData = new CombinedData();
for (LineData lineData : lineDatas) {
combinedData.setData(lineData);
}
chart.setData(combinedData);
chart.invalidate();
}
/**
* 獲取LineDataSet
*
* @param entries
* @param label
* @param textColor
* @param lineColor
* @return
*/
public static LineDataSet getLineData(List<Entry> entries, String label, @ColorInt int textColor, @ColorInt int lineColor, boolean isFill) {
LineDataSet dataSet = new LineDataSet(entries, label);
// 設定曲線的顏色
dataSet.setColor(lineColor);
//數值文字顏色
dataSet.setValueTextColor(textColor);
// 模式為貝塞爾曲線
dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
// 是否繪製資料值
dataSet.setDrawValues(false);
// 是否繪製圓點
dataSet.setDrawCircles(true);
dataSet.setDrawCircleHole(false);
// 這裡有一個坑,當我們想隱藏掉高亮線的時候,MarkerView 跟著不見了
// 因此只有將它設定成透明色
dataSet.setHighlightEnabled(true);// 隱藏點選時候的高亮線
//設定高亮線為透明色
dataSet.setHighLightColor(Color.TRANSPARENT);
if (isFill) {
//是否設定填充曲線到x軸之間的區域
dataSet.setDrawFilled(true);
// 填充顏色
dataSet.setFillColor(lineColor);
}
//設定圓點的顏色
dataSet.setCircleColor(lineColor);
// 設定圓點半徑
dataSet.setCircleRadius(3.5f);
// 設定線的寬度
dataSet.setLineWidth(1f);
return dataSet;
}
/**
* 獲取barDataSet
* @param entries
* @param label
* @param textColor
* @param lineColor
* @return
*/
public static BarDataSet getBarDataSet(List<BarEntry> entries, String label, @ColorInt int textColor, @ColorInt int lineColor) {
BarDataSet dataSet = new BarDataSet(entries, label);
dataSet.setBarBorderWidth(5);
dataSet.setBarShadowColor(lineColor);
dataSet.setValueTextColor(textColor);
dataSet.setDrawValues(false);
return dataSet;
}
/**
* 配置柱狀圖基礎設定
* @param barChart
* @param xLabels
*/
public static void configBarChart(BarChart barChart, final List<String> xLabels) {
barChart.getDescription().setEnabled(false);//設定描述
barChart.setPinchZoom(false);//設定按比例放縮柱狀圖
barChart.setScaleEnabled(false);
barChart.setDragEnabled(true);
barChart.setNoDataText(""); // 沒有資料時的提示文案
//x座標軸設定
// IAxisValueFormatter xAxisFormatter = new StringAxisValueFormatter(xAxisValue);//設定自定義的x軸值格式化器
XAxis xAxis = barChart.getXAxis();//獲取x軸
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);//設定X軸標籤顯示位置
xAxis.setDrawGridLines(false);//不繪製格網線
xAxis.setGranularity(1f);//設定最小間隔,防止當放大時,出現重複標籤。
// 顯示x軸標籤
IAxisValueFormatter formatter = new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return xLabels.get(Math.min(Math.max((int) value, 0), xLabels.size() - 1));
}
};
xAxis.setValueFormatter(formatter);
xAxis.setTextSize(10);//設定標籤字型大小
xAxis.setTextColor(barChart.getResources().getColor(R.color.char_text_color));
xAxis.setAxisLineColor(Color.parseColor("#4cffffff"));
xAxis.setLabelCount(xLabels.size());//設定標籤顯示的個數
//y軸設定
YAxis leftAxis = barChart.getAxisLeft();//獲取左側y軸
leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);//設定y軸標籤顯示在外側
leftAxis.setAxisMinimum(0f);//設定Y軸最小值
leftAxis.setDrawGridLines(false);
leftAxis.setDrawLabels(true);//禁止繪製y軸標籤
leftAxis.setDrawAxisLine(false);//禁止繪製y軸
leftAxis.setAxisLineColor(Color.parseColor("#4cffffff"));
leftAxis.setTextColor(barChart.getResources().getColor(R.color.char_text_color));
leftAxis.setValueFormatter(new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return ((int) (value * 100)) + "%";
}
});
barChart.getAxisRight().setEnabled(false);//禁用右側y軸
barChart.getLegend().setEnabled(false);
//圖例設定
/* Legend legend = barChart.getLegend();
legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER);//圖例水平居中
legend.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP);//圖例在圖表上方
legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);//圖例的方向為水平
legend.setDrawInside(false);//繪製在chart的外側
legend.setDirection(Legend.LegendDirection.LEFT_TO_RIGHT);//圖例中的文字方向
legend.setForm(Legend.LegendForm.SQUARE);//圖例窗體的形狀
legend.setFormSize(0f);//圖例窗體的大小
legend.setTextSize(16f);//圖例文字的大小*/
//legend.setYOffset(-2f);
Matrix matrix = new Matrix();
// 根據資料量來確定 x軸縮放大倍
if (xLabels.size() <= 10) {
matrix.postScale(1.0f, 1.0f);
} else if (xLabels.size() <= 15) {
matrix.postScale(1.5f, 1.0f);
} else if (xLabels.size() <= 20) {
matrix.postScale(2.0f, 1.0f);
} else {
matrix.postScale(3.0f, 1.0f);
}
barChart.getViewPortHandler().refresh(matrix, barChart, false);
barChart.setExtraBottomOffset(10);//距檢視視窗底部的偏移,類似與paddingbottom
barChart.setExtraTopOffset(30);//距檢視視窗頂部的偏移,類似與paddingtop
barChart.setFitBars(true);//使兩側的柱圖完全顯示
barChart.animateX(1500);//資料顯示動畫,從左往右依次顯示
}
/**
* 初始化柱狀圖圖表資料
* @param chart
* @param entries
* @param title
* @param barColor
*/
public static void initBarChart(BarChart chart, List<BarEntry> entries, String title, @ColorInt int barColor) {
BarDataSet set1 = new BarDataSet(entries, title);
set1.setValueTextColor(Color.WHITE);
set1.setColor(barColor);
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(set1);
BarData data = new BarData(dataSets);
data.setValueTextSize(10f);
// 設定bar的寬度,但是點很多少的時候好像沒作用,會拉得很寬
data.setBarWidth(0.1f);
// 設定value值 顏色
data.setValueTextColor(Color.WHITE);
//設定y軸顯示的標籤
data.setValueFormatter(new IValueFormatter() {
@Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
return ((int) (value * 100)) + "%";
}
});
chart.setData(data);
chart.invalidate();
}
}
複製程式碼
最終使用
最後,呼叫工具類的三個方法,三行程式碼就ok :
// 1.配置基礎圖表配置
MPChartUtils.configChart(mWeightChart, xLabels, maxWeight, minWeight, true);
// 2,獲取資料Data,這裡有2條曲線
LineDataSet tartgetDataSet = MPChartUtils.getLineData(targetEntries, "目標體重", Color.WHITE, Color.WHITE, false);
LineDataSet lineDataSet = MPChartUtils.getLineData(weightEntries, "當前體重", Color.WHITE, getResources().getColor(R.color.chart_dot_color), false);
// 3,初始化資料並繪製
MPChartUtils.initData(mWeightChart, new LineData(lineDataSet, tartgetDataSet));
複製程式碼
更多Android乾貨文章,關注公眾號 【Android技術雜貨鋪】