MPAndroidChart 是 Android 平台最流行的開源圖表庫,支援線圖、餅圖、柱狀圖、散點圖、燭台圖、雷達圖等,提供拖拽縮放、動畫、圖例、標記等豐富交互功能。
// build.gradle (Module: app)
dependencies {
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
// 或下載 jar 放入 libs 目錄
}
需在專案級 build.gradle 添加 JitPack 倉庫:
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
<com.github.mikephil.charting.charts.PieChart
android:id="@+id/spread_pie_chart"
android:layout_width="match_parent"
android:layout_height="300dp" />
public class PieChartActivity extends AppCompatActivity {
private PieChart mChart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pie_chart);
mChart = findViewById(R.id.spread_pie_chart);
PieData pieData = getPieData(4, 100);
showChart(mChart, pieData);
}
private void showChart(PieChart pieChart, PieData pieData) {
// 基礎外觀
pieChart.setHoleColorTransparent(true);
pieChart.setHoleRadius(60f); // 中心空洞半徑
pieChart.setTransparentCircleRadius(64f); // 透明圈半徑
pieChart.setDescription("2024 季度營收分布");
pieChart.setDrawCenterText(true);
pieChart.setCenterText("季度營收"); // 中心文字
pieChart.setDrawHoleEnabled(true);
pieChart.setRotationAngle(90); // 起始旋轉角度
pieChart.setRotationEnabled(true); // 允許手勢旋轉
pieChart.setUsePercentValues(true); // 顯示百分比
// 圖例配置
Legend legend = pieChart.getLegend();
legend.setPosition(Legend.LegendPosition.RIGHT_OF_CHART);
legend.setXEntrySpace(7f);
legend.setYEntrySpace(5f);
// 動畫
pieChart.animateXY(1000, 1000);
pieChart.setData(pieData);
pieChart.invalidate(); // 重繪
}
private PieData getPieData(int count, float range) {
List xValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
xValues.add("Q" + (i + 1)); // 標籤
}
List yValues = new ArrayList<>();
// 模擬數據:四季度佔比
yValues.add(new PieEntry(14f, 0)); // 14%
yValues.add(new PieEntry(14f, 1));
yValues.add(new PieEntry(34f, 2));
yValues.add(new PieEntry(38f, 3));
PieDataSet dataSet = new PieDataSet(yValues, "2024 季度營收");
dataSet.setSliceSpace(3f); // 扇區間距
dataSet.setSelectionShift(5f); // 選中突出距離
// 自定義顏色
List colors = Arrays.asList(
Color.rgb(205, 205, 205),
Color.rgb(114, 188, 223),
Color.rgb(255, 123, 124),
Color.rgb(57, 135, 200)
);
dataSet.setColors(colors);
// 數值格式化
dataSet.setValueFormatter(new PercentFormatter(pieChart));
dataSet.setValueTextSize(12f);
dataSet.setValueTextColor(Color.WHITE);
return new PieData(xValues, dataSet);
}
}
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/spread_line_chart"
android:layout_width="match_parent"
android:layout_height="300dp" />
public class LineChartActivity extends AppCompatActivity {
private LineChart mLineChart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_line_chart);
mLineChart = findViewById(R.id.spread_line_chart);
LineData lineData = getLineData(30, 100); // 30個點
showChart(mLineChart, lineData, Color.rgb(114, 188, 223));
}
private void showChart(LineChart chart, LineData data, int color) {
chart.setDrawBorders(false);
chart.setDescription("");
chart.setNoDataTextDescription("暫無數據");
chart.setDrawGridBackground(false);
chart.setGridBackgroundColor(Color.WHITE & 0x70FFFFFF);
// 交互啟用
chart.setTouchEnabled(true);
chart.setDragEnabled(true);
chart.setScaleEnabled(true);
chart.setPinchZoom(false);
// 圖例
Legend legend = chart.getLegend();
legend.setForm(Legend.LegendForm.CIRCLE);
legend.setFormSize(6f);
legend.setTextColor(Color.WHITE);
chart.setData(data);
chart.animateX(2500); // X軸動畫
chart.invalidate();
}
private LineData getLineData(int count, float range) {
List xValues = new ArrayList<>();
for (int i = 0; i < count; i++) xValues.add("" + i);
List yValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
float val = (float) (Math.random() * range) + 3;
yValues.add(new Entry(i, val)); // x 為索引
}
LineDataSet dataSet = new LineDataSet(yValues, "測試折線圖");
dataSet.setLineWidth(2f);
dataSet.setCircleRadius(3f);
dataSet.setColor(Color.WHITE);
dataSet.setCircleColor(Color.WHITE);
dataSet.setHighLightColor(Color.rgb(255, 255, 0));
dataSet.setDrawValues(true); // 顯示數值
dataSet.setValueTextSize(10f);
// 填充區域
dataSet.setDrawFilled(true);
dataSet.setFillColor(Color.rgb(114, 188, 223));
dataSet.setFillAlpha(80);
return new LineData(xValues, dataSet);
}
}
<com.github.mikephil.charting.charts.BarChart
android:id="@+id/spread_bar_chart"
android:layout_width="match_parent"
android:layout_height="300dp" />
public class BarChartActivity extends AppCompatActivity {
private BarChart mBarChart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bar_chart);
mBarChart = findViewById(R.id.spread_bar_chart);
BarData barData = getBarData(4, 100);
showBarChart(mBarChart, barData);
}
private void showBarChart(BarChart chart, BarData data) {
chart.setDrawBorders(false);
chart.setDescription("");
chart.setNoDataTextDescription("暫無數據");
chart.setDrawGridBackground(false);
chart.setGridBackgroundColor(Color.WHITE & 0x70FFFFFF);
chart.setTouchEnabled(true);
chart.setDragEnabled(true);
chart.setScaleEnabled(true);
chart.setPinchZoom(false);
chart.setDrawBarShadow(true); // 陰影
Legend legend = chart.getLegend();
legend.setForm(Legend.LegendForm.CIRCLE);
legend.setFormSize(6f);
legend.setTextColor(Color.BLACK);
// X軸配置
XAxis xAxis = chart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(false);
xAxis.setGranularity(1f);
chart.setData(data);
chart.animateY(2500); // Y軸動畫
chart.invalidate();
}
private BarData getBarData(int count, float range) {
List xValues = new ArrayList<>();
for (int i = 0; i < count; i++) xValues.add("Q" + (i + 1));
List yValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
float val = (float) (Math.random() * range) + 3;
yValues.add(new BarEntry(i, val));
}
BarDataSet dataSet = new BarDataSet(yValues, "季度營收");
dataSet.setColor(Color.rgb(114, 188, 223));
dataSet.setValueTextSize(12f);
dataSet.setValueTextColor(Color.BLACK);
return new BarData(xValues, dataSet);
}
}
// 保留一位小數
dataSet.setValueFormatter(new DefaultValueFormatter(1));
// 自定義格式化
dataSet.setValueFormatter(new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
return String.format("%,.0f", value) + "萬";
}
});
MarkerView mv = new CustomMarkerView(this, R.layout.custom_marker);
mv.setChartView(chart);
chart.setMarker(mv);
chart.setScaleMinima(1f, 1f); // 最小縮放
chart.setVisibleXRangeMaximum(20); // 最大可見 X 軸範圍
LineDataSet set1 = new LineDataSet(data1, "數據集1");
LineDataSet set2 = new LineDataSet(data2, "數據集2");
LineData combinedData = new LineData(set1, set2);
new Thread(() -> {
while (running) {
runOnUiThread(() -> {
Entry newEntry = new Entry(currentIndex++, randomValue());
dataSet.addEntry(newEntry);
chart.notifyDataSetChanged();
chart.moveViewToX(currentIndex - 20); // 滾動視窗
});
Thread.sleep(1000);
}
}).start();
| 問題 | 解決方案 |
|---|---|
| 大量數據渲染卡頓 | 啟用 setDrawValues(false)、降低幀率、使用 setViewportJob 分批渲染 |
| X軸標籤重疊 | xAxis.setLabelRotationAngle(-45)、setGranularity、setLabelCount |
| 記憶體洩漏 | Activity 銷毀時調用 chart.clear()、移除監聽器 |
| 動畫不流暢 | 關閉硬體加速 chart.setLayerType(LAYER_TYPE_SOFTWARE, null) |
| 導出圖片 | chart.saveToGallery("title", 50) 或 chart.getChartBitmap() |
com.github.mikephil.charting → com.github.mikephil.charting (相同)Entry 建構子:new Entry(value, xIndex) → new Entry(x, y)BarEntry、PieEntry 同理DataSet 方法重命名:setColors → setColor (單色)、setColors(List) (多色)LineData(String[] labels, List) 建構子,改用 LineData(List) + xAxis.setValueFormatter