MPAndroidChart 用法解析和性能优化 - Kotlin Java 双版本
目录
- 一、 基础配置对比
- 1.依赖添加
- 2.布局定义
- 二、 折线图完整实现
- Kotlin 版本
- Java 版本
- 三、 饼图完整实现
- Kotlin 版本
- Java 版本
- 四、 高级功能实现
- 1.自定义 MarkerView
- Kotlin 版本
- Java 版本
- 2. 限制线配置
- Kotlin 版本
- Java 版本
- 五、 性能优化实现
- 1.大数据量处理
- Kotlin 版本
- Java 版本
- 2. 动态数据更新
- Kotlin 版本
- Java 版本
- 3. 内存管理优化
- Kotlin 版本
- Java 版本
- 4. 渲染优化
- Kotlin 版本
- Java 版本
- 5. 调试与监控
- Kotlin 版本
- Java 版本
- 六、性能优化检查清单
- 1.必须实施的优化措施
- 2. 性能陷阱提醒
- 七、Java VS Kotlin 总结
一、 基础配置对比
1.依赖添加
Gradle 配置,两者相同
dependencies {implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
}
2.布局定义
XML,两者相同
<com.github.mikephil.charting.charts.LineChartandroid:id="@+id/lineChart"android:layout_width="match_parent"android:layout_height="300dp" />
二、 折线图完整实现
Kotlin 版本
class ChartActivity : AppCompatActivity() {private lateinit var lineChart: LineChartoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_chart)lineChart = findViewById(R.id.lineChart)setupLineChart()setLineChartData()}private fun setupLineChart() {// 基础配置lineChart.apply {setTouchEnabled(true)setPinchZoom(true)description.isEnabled = falselegend.isEnabled = false}// X轴配置val xAxis = lineChart.xAxisxAxis.apply {position = XAxis.XAxisPosition.BOTTOMsetDrawGridLines(false)granularity = 1ftextSize = 12ftextColor = Color.GRAY// 自定义标签格式化valueFormatter = object : ValueFormatter() {override fun getAxisLabel(value: Float, axis: AxisBase?): String {return "${value.toInt()}月"}}}// Y轴配置lineChart.axisRight.isEnabled = falseval leftAxis = lineChart.axisLeftleftAxis.apply {setDrawGridLines(true)axisMinimum = 0faxisMaximum = 200ftextSize = 12ftextColor = Color.GRAY}}private fun setLineChartData() {// 创建模拟数据val entries = ArrayList<Entry>().apply {add(Entry(0f, 45f))add(Entry(1f, 68f))add(Entry(2f, 92f))add(Entry(3f, 57f))add(Entry(4f, 103f))add(Entry(5f, 78f))}// 配置数据集val dataSet = LineDataSet(entries, "月度数据").apply {color = ContextCompat.getColor(this@ChartActivity, R.color.chart_line)lineWidth = 2fsetDrawCircles(true)circleRadius = 4fcircleHoleRadius = 2fcircleColors = listOf(ContextCompat.getColor(this@ChartActivity, R.color.chart_circle))setDrawValues(false)mode = LineDataSet.Mode.CUBIC_BEZIER // 曲线模式}// 设置数据并刷新val lineData = LineData(dataSet)lineChart.data = lineDatalineChart.invalidate()// 添加动画lineChart.animateY(1000)}
}
Java 版本
public class ChartActivity extends AppCompatActivity {private LineChart lineChart;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_chart);lineChart = findViewById(R.id.lineChart);setupLineChart();setLineChartData();}private void setupLineChart() {// 基础配置lineChart.setTouchEnabled(true);lineChart.setPinchZoom(true);lineChart.getDescription().setEnabled(false);lineChart.getLegend().setEnabled(false);// X轴配置XAxis xAxis = lineChart.getXAxis();xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);xAxis.setDrawGridLines(false);xAxis.setGranularity(1f);xAxis.setTextSize(12f);xAxis.setTextColor(Color.GRAY);// 自定义标签格式化xAxis.setValueFormatter(new ValueFormatter() {@Overridepublic String getAxisLabel(float value, AxisBase axis) {return (int)value + "月";}});// Y轴配置lineChart.getAxisRight().setEnabled(false);YAxis leftAxis = lineChart.getAxisLeft();leftAxis.setDrawGridLines(true);leftAxis.setAxisMinimum(0f);leftAxis.setAxisMaximum(200f);leftAxis.setTextSize(12f);leftAxis.setTextColor(Color.GRAY);}private void setLineChartData() {// 创建模拟数据List<Entry> entries = new ArrayList<>();entries.add(new Entry(0f, 45f));entries.add(new Entry(1f, 68f));entries.add(new Entry(2f, 92f));entries.add(new Entry(3f, 57f));entries.add(new Entry(4f, 103f));entries.add(new Entry(5f, 78f));// 配置数据集LineDataSet dataSet = new LineDataSet(entries, "月度数据");dataSet.setColor(ContextCompat.getColor(this, R.color.chart_line));dataSet.setLineWidth(2f);dataSet.setDrawCircles(true);dataSet.setCircleRadius(4f);dataSet.setCircleHoleRadius(2f);dataSet.setCircleColor(ContextCompat.getColor(this, R.color.chart_circle));dataSet.setDrawValues(false);dataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);// 设置数据并刷新LineData lineData = new LineData(dataSet);lineChart.setData(lineData);lineChart.invalidate();// 添加动画lineChart.animateY(1000);}
}
三、 饼图完整实现
Kotlin 版本
class PieChartActivity : AppCompatActivity() {private lateinit var pieChart: PieChartoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_pie_chart)pieChart = findViewById(R.id.pieChart)setupPieChart()setPieChartData()}private fun setupPieChart() {pieChart.apply {setUsePercentValues(true) // 显示百分比description.isEnabled = falsesetDrawEntryLabels(false) // 不显示条目标签setDrawCenterText(true)centerText = "成绩分布"setCenterTextSize(18f)setCenterTextColor(Color.BLACK)// 设置边距防止标签被裁剪setExtraOffsets(20f, 5f, 20f, 5f)}// 图例配置val legend = pieChart.legendlegend.apply {verticalAlignment = Legend.LegendVerticalAlignment.CENTERhorizontalAlignment = Legend.LegendHorizontalAlignment.RIGHTorientation = Legend.LegendOrientation.VERTICALsetDrawInside(false)textSize = 12f}// 启用旋转pieChart.isRotationEnabled = true}private fun setPieChartData() {val entries = listOf(PieEntry(25f, "优秀"),PieEntry(35f, "良好"),PieEntry(20f, "中等"),PieEntry(15f, "及格"),PieEntry(5f, "不及格"))val dataSet = PieDataSet(entries, "").apply {colors = getChartColors()valueTextSize = 12fvalueTextColor = Color.WHITEsetDrawIcons(false)sliceSpace = 2f // 扇形间距selectionShift = 5f // 选中时突出距离}val pieData = PieData(dataSet).apply {setValueFormatter(PercentFormatter(pieChart))}pieChart.data = pieDatapieChart.invalidate()// 添加旋转动画pieChart.animateY(1000, Easing.EaseInOutCubic)}private fun getChartColors(): List<Int> {return listOf(Color.parseColor("#FF6B6B"), // 红色Color.parseColor("#4ECDC4"), // 青色Color.parseColor("#45B7D1"), // 蓝色Color.parseColor("#96CEB4"), // 绿色Color.parseColor("#FFEAA7") // 黄色)}
}
Java 版本
public class PieChartActivity extends AppCompatActivity {private PieChart pieChart;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pie_chart);pieChart = findViewById(R.id.pieChart);setupPieChart();setPieChartData();}private void setupPieChart() {// 基础配置pieChart.setUsePercentValues(true);pieChart.getDescription().setEnabled(false);pieChart.setDrawEntryLabels(false);pieChart.setDrawCenterText(true);pieChart.setCenterText("成绩分布");pieChart.setCenterTextSize(18f);pieChart.setCenterTextColor(Color.BLACK);// 设置边距pieChart.setExtraOffsets(20, 5, 20, 5);// 图例配置Legend legend = pieChart.getLegend();legend.setVerticalAlignment(Legend.LegendVerticalAlignment.CENTER);legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT);legend.setOrientation(Legend.LegendOrientation.VERTICAL);legend.setDrawInside(false);legend.setTextSize(12f);// 启用旋转pieChart.setRotationEnabled(true);}private void setPieChartData() {List<PieEntry> entries = new ArrayList<>();entries.add(new PieEntry(25f, "优秀"));entries.add(new PieEntry(35f, "良好"));entries.add(new PieEntry(20f, "中等"));entries.add(new PieEntry(15f, "及格"));entries.add(new PieEntry(5f, "不及格"));PieDataSet dataSet = new PieDataSet(entries, "");dataSet.setColors(getChartColors());dataSet.setValueTextSize(12f);dataSet.setValueTextColor(Color.WHITE);dataSet.setDrawIcons(false);dataSet.setSliceSpace(2f);dataSet.setSelectionShift(5f);PieData pieData = new PieData(dataSet);pieData.setValueFormatter(new PercentFormatter(pieChart));pieChart.setData(pieData);pieChart.invalidate();// 添加动画pieChart.animateY(1000, Easing.EaseInOutCubic);}private List<Integer> getChartColors() {List<Integer> colors = new ArrayList<>();colors.add(Color.parseColor("#FF6B6B"));colors.add(Color.parseColor("#4ECDC4"));colors.add(Color.parseColor("#45B7D1"));colors.add(Color.parseColor("#96CEB4"));colors.add(Color.parseColor("#FFEAA7"));return colors;}
}
四、 高级功能实现
1.自定义 MarkerView
Kotlin 版本
class CustomMarkerView(context: Context, layoutResource: Int) : MarkerView(context, layoutResource) {private val tvContent: TextView = findViewById(R.id.tvContent)override fun refreshContent(e: Entry?, highlight: Highlight?) {e?.let {tvContent.text = "数值: ${it.y.toInt()}"}super.refreshContent(e, highlight)}override fun getOffset(): MPPointF {// 调整位置:水平居中,显示在点上方return MPPointF(-(width / 2).toFloat(), -height.toFloat())}
}// 使用 MarkerView
private fun setupMarkerView() {val markerView = CustomMarkerView(this, R.layout.custom_marker_view)markerView.chartView = lineChartlineChart.marker = markerView
}
Java 版本
public class CustomMarkerView extends MarkerView {private TextView tvContent;public CustomMarkerView(Context context, int layoutResource) {super(context, layoutResource);tvContent = findViewById(R.id.tvContent);}@Overridepublic void refreshContent(Entry e, Highlight highlight) {tvContent.setText(String.format("数值: %.0f", e.getY()));super.refreshContent(e, highlight);}@Overridepublic MPPointF getOffset() {return new MPPointF(-(getWidth() / 2), -getHeight());}
}// 使用 MarkerView
private void setupMarkerView() {CustomMarkerView markerView = new CustomMarkerView(this, R.layout.custom_marker_view);markerView.setChartView(lineChart);lineChart.setMarker(markerView);
}
2. 限制线配置
Kotlin 版本
private fun setupLimitLine() {val leftAxis = lineChart.axisLeft// 创建平均值限制线val averageLine = LimitLine(75f, "平均值").apply {lineWidth = 2flineColor = Color.REDtextColor = Color.REDtextSize = 12fenableDashedLine(10f, 10f, 0f) // 虚线}// 清除旧线并添加新线leftAxis.removeAllLimitLines()leftAxis.addLimitLine(averageLine)// 确保坐标轴范围包含限制线if (75f > leftAxis.axisMaximum) {leftAxis.axisMaximum = 85f}
}
Java 版本
private void setupLimitLine() {YAxis leftAxis = lineChart.getAxisLeft();// 创建平均值限制线LimitLine averageLine = new LimitLine(75f, "平均值");averageLine.setLineWidth(2f);averageLine.setLineColor(Color.RED);averageLine.setTextColor(Color.RED);averageLine.setTextSize(12f);averageLine.enableDashedLine(10f, 10f, 0f);// 清除旧线并添加新线leftAxis.removeAllLimitLines();leftAxis.addLimitLine(averageLine);// 确保坐标轴范围包含限制线if (75f > leftAxis.getAxisMaximum()) {leftAxis.setAxisMaximum(85f);}
}
五、 性能优化实现
1.大数据量处理
Kotlin 版本
private fun handleLargeDataSet() {val rawData = generateLargeDataSet(10000) // 生成10000个数据点// 数据抽样val sampledData = if (rawData.size > 1000) {sampleData(rawData, 10) // 每10个点取1个} else {rawData}val dataSet = LineDataSet(sampledData, "大数据集").apply {setDrawCircles(false) // 不绘制圆圈提升性能setDrawValues(false)color = Color.BLUElineWidth = 1f}// 在后台线程设置数据lifecycleScope.launch(Dispatchers.Default) {val lineData = LineData(dataSet)withContext(Dispatchers.Main) {lineChart.data = lineDatalineChart.invalidate()}}
}private fun sampleData(original: List<Entry>, interval: Int): List<Entry> {return original.filterIndexed { index, _ -> index % interval == 0 }
}
Java 版本
private void handleLargeDataSet() {List<Entry> rawData = generateLargeDataSet(10000);// 数据抽样List<Entry> sampledData;if (rawData.size() > 1000) {sampledData = sampleData(rawData, 10);} else {sampledData = rawData;}LineDataSet dataSet = new LineDataSet(sampledData, "大数据集");dataSet.setDrawCircles(false);dataSet.setDrawValues(false);dataSet.setColor(Color.BLUE);dataSet.setLineWidth(1f);// 使用AsyncTask在后台处理new AsyncTask<Void, Void, LineData>() {@Overrideprotected LineData doInBackground(Void... voids) {return new LineData(dataSet);}@Overrideprotected void onPostExecute(LineData lineData) {lineChart.setData(lineData);lineChart.invalidate();}}.execute();
}private List<Entry> sampleData(List<Entry> original, int interval) {List<Entry> sampled = new ArrayList<>();for (int i = 0; i < original.size(); i++) {if (i % interval == 0) {sampled.add(original.get(i));}}return sampled;
}
2. 动态数据更新
Kotlin 版本
private fun updateChartData(newValue: Float) {val data = lineChart.dataval dataSet = data.getDataSetByIndex(0) as LineDataSet// 添加新数据点val lastX = dataSet.getEntryForIndex(dataSet.entryCount - 1)?.x ?: 0fdataSet.addEntry(Entry(lastX + 1, newValue))// 限制数据点数量if (dataSet.entryCount > 50) {dataSet.removeFirst()}// 通知数据变化data.notifyDataChanged()lineChart.notifyDataSetChanged()lineChart.invalidate()// 自动滚动到最新位置lineChart.moveViewToX(data.xMax)
}
Java 版本
private void updateChartData(float newValue) {LineData data = lineChart.getData();if (data != null) {LineDataSet dataSet = (LineDataSet) data.getDataSetByIndex(0);// 添加新数据点float lastX = 0;if (dataSet.getEntryCount() > 0) {lastX = dataSet.getEntryForIndex(dataSet.getEntryCount() - 1).getX();}dataSet.addEntry(new Entry(lastX + 1, newValue));// 限制数据点数量if (dataSet.getEntryCount() > 50) {dataSet.removeFirst();}// 通知数据变化data.notifyDataChanged();lineChart.notifyDataSetChanged();lineChart.invalidate();// 自动滚动到最新位置lineChart.moveViewToX(data.getXMax());}
}
3. 内存管理优化
Kotlin 版本
class ChartMemoryManager : LifecycleObserver {private var chart: LineChart? = null@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)fun cleanup() {chart?.let {it.data = null // 清除数据引用it.clear() // 清理图表it.setOnTouchListener(null) // 移除触摸监听}chart = null}fun setupChartWithMemoryManagement(chart: LineChart) {this.chart = chart// 禁用硬件加速(在某些设备上可减少内存使用)chart.setLayerType(View.LAYER_TYPE_SOFTWARE, null)// 限制缓存大小chart.setMaxVisibleValueCount(100)}// 分页加载大数据fun loadDataInPages(chart: LineChart, allData: List<Entry>, pageSize: Int = 1000) {val pages = allData.chunked(pageSize)var currentPage = 0fun loadNextPage() {if (currentPage < pages.size) {val pageData = pages[currentPage]updateChartData(chart, pageData)currentPage++// 延迟加载下一页chart.postDelayed({ loadNextPage() }, 100)}}loadNextPage()}
}
Java 版本
public class ChartMemoryManager implements LifecycleObserver {private LineChart chart;@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)public void cleanup() {if (chart != null) {chart.setData(null);chart.clear();chart.setOnTouchListener(null);}chart = null;}public void setupChartWithMemoryManagement(LineChart chart) {this.chart = chart;chart.setLayerType(View.LAYER_TYPE_SOFTWARE, null);chart.setMaxVisibleValueCount(100);}
}
4. 渲染优化
Kotlin 版本
class ChartRenderOptimizer {companion object {// 预定义颜色避免重复创建val CHART_COLORS = intArrayOf(Color.parseColor("#FF6B6B"),Color.parseColor("#4ECDC4"),Color.parseColor("#45B7D1"),Color.parseColor("#96CEB4"))}fun optimizeRendering(chart: LineChart) {chart.apply {// 关闭不必要的绘制setDrawMarkers(false)setDrawBorders(false)setDrawGridBackground(false)// 优化坐标轴绘制xAxis.setDrawGridLines(false)axisLeft.setDrawGridLines(true)axisLeft.setDrawZeroLine(false)// 使用预定义颜色data?.dataSets?.forEachIndexed { index, dataSet ->val colorIndex = index % CHART_COLORS.sizewhen (dataSet) {is LineDataSet -> dataSet.color = CHART_COLORS[colorIndex]is BarDataSet -> dataSet.color = CHART_COLORS[colorIndex]}}}}// 动态调整渲染质量fun adjustRenderingQuality(chart: LineChart, isHighQuality: Boolean) {chart.data?.dataSets?.forEach { dataSet ->when (dataSet) {is LineDataSet -> {dataSet.setDrawCircles(isHighQuality)dataSet.setDrawValues(isHighQuality)dataSet.mode = if (isHighQuality) LineDataSet.Mode.CUBIC_BEZIER else LineDataSet.Mode.LINEAR}}}}
}
Java 版本
public class ChartRenderOptimizer {private static final int[] CHART_COLORS = {Color.parseColor("#FF6B6B"),Color.parseColor("#4ECDC4"),Color.parseColor("#45B7D1"),Color.parseColor("#96CEB4")};public void optimizeRendering(LineChart chart) {chart.setDrawMarkers(false);chart.setDrawBorders(false);chart.setDrawGridBackground(false);chart.getXAxis().setDrawGridLines(false);chart.getAxisLeft().setDrawGridLines(true);chart.getAxisLeft().setDrawZeroLine(false);if (chart.getData() != null) {for (int i = 0; i < chart.getData().getDataSetCount(); i++) {IDataSet dataSet = chart.getData().getDataSetByIndex(i);int colorIndex = i % CHART_COLORS.length;if (dataSet instanceof LineDataSet) {((LineDataSet) dataSet).setColor(CHART_COLORS[colorIndex]);} else if (dataSet instanceof BarDataSet) {((BarDataSet) dataSet).setColor(CHART_COLORS[colorIndex]);}}}}
}
5. 调试与监控
Kotlin 版本
class ChartDebugHelper {companion object {const val DEBUG_TAG = "MPAndroidChart"}fun logChartPerformance(chart: LineChart, tag: String = DEBUG_TAG) {val data = chart.dataif (data != null) {val totalEntries = data.dataSets.sumBy { it.entryCount }Log.d(tag, "图表数据统计:")Log.d(tag, "数据集数量: ${data.dataSetCount}")Log.d(tag, "总数据点: $totalEntries")Log.d(tag, "X轴范围: ${data.xMin} - ${data.xMax}")Log.d(tag, "Y轴范围: ${data.yMin} - ${data.yMax}")}}// 内存使用监控fun monitorMemoryUsage() {val runtime = Runtime.getRuntime()val usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024)val maxMemory = runtime.maxMemory() / (1024 * 1024)Log.d(DEBUG_TAG, "内存使用: ${usedMemory}MB / ${maxMemory}MB")}
}
Java 版本
public class ChartDebugHelper {private static final String DEBUG_TAG = "MPAndroidChart";public static void logChartPerformance(LineChart chart) {LineData data = chart.getData();if (data != null) {int totalEntries = 0;for (int i = 0; i < data.getDataSetCount(); i++) {totalEntries += data.getDataSetByIndex(i).getEntryCount();}Log.d(DEBUG_TAG, "图表数据统计:");Log.d(DEBUG_TAG, "数据集数量: " + data.getDataSetCount());Log.d(DEBUG_TAG, "总数据点: " + totalEntries);Log.d(DEBUG_TAG, "X轴范围: " + data.getXMin() + " - " + data.getXMax());Log.d(DEBUG_TAG, "Y轴范围: " + data.getYMin() + " - " + data.getYMax());}}
}
六、性能优化检查清单
1.必须实施的优化措施
1. 数据层面
- 大数据集进行抽样显示(>1000个点)
- 关闭不必要的绘制元素(圆圈、数值标签)
- 使用合适的数据结构存储数据
2. 渲染层面
- 禁用不需要的交互功能
- 简化坐标轴和图例
- 使用预定义颜色避免重复创建
3. 内存层面
- 在 onDestroy 中清理图表资源
- 限制可见数据点数量
- 避免在 RecyclerView 中过度使用图表
2. 性能陷阱提醒
- 避免在主线程处理大数据
- 不要频繁调用 invalidate()
- 谨慎使用动画效果
- 注意 RecyclerView 中的图表复用
- 及时清理不需要的监听器
七、Java VS Kotlin 总结
特性 | Kotlin 版本 | Java 版本 |
---|---|---|
空安全 | 使用 lateinit 和 ?. 操作符,更安全 | 需要手动进行 null 检查 |
集合操作 | 支持 apply 、let 、filterIndexed 等扩展函数 | 依赖传统循环和条件判断 |
代码简洁性 | 语法简洁,减少样板代码 | 相对冗长,需更多模板代码 |
异步处理 | 使用协程(lifecycleScope + Dispatchers ) | 使用 AsyncTask 或 Thread /Handler |
资源管理 | 自动管理生命周期与协程作用域 | 需手动管理线程与资源释放 |
结论:两种语言在功能实现上完全一致,主要差异在于语法特性和现代编程范式的支持。Kotlin 版本通常更加简洁、安全、可读性强,而 Java 版本更传统、直观,适合已有 Java 基础的开发者。
📌 建议:新项目推荐使用 Kotlin + 协程,兼顾性能、可维护性与开发效率。