产品需求,需要增加一个身高体重曲线的模块,设计图如下
体重曲线也是这样。
先来一张实现的粗略效果图
曲线图,折线图等,大多使用流行强大的MPandroidChart
库,这里也是。
在网上找了一些类似的效果,都不是很符合设计图,
直接附上代码,注释都很清楚。主要调用库的方法
mLineChart = binding.lineChart
mLineChart.setDrawBorders(true)
var xAxis = mLineChart.xAxis
/*设置X轴的位置(默认在上方)*/
xAxis.position = XAxis.XAxisPosition.BOTTOM
/*设置X轴坐标之间的最小间隔*/
xAxis.granularity = 1f
xAxis.mAxisMaximum = 72f
/*设置X轴值为字符串*/
xAxis.setValueFormatter(object : IAxisValueFormatter {
override fun getFormattedValue(value: Float, axis: AxisBase?): String {
return PinyinUtils.getFormatXLabel(value.toInt())
}
})
/* 右侧Y轴不显示*/
var axisRight = mLineChart.axisRight
axisRight.isEnabled = false
/*隐藏图例*/
mLineChart.legend.isEnabled = false
/*隐藏描述*/
var description = Description()
description.isEnabled = false
mLineChart.description = description
/*X,Y轴同时缩放,false则X,Y轴单独缩放,默认false*/
// mLineChart.setPinchZoom(true);
mLineChart.setScaleEnabled(false)
// 重置所有缩放与拖动,使图标完全符合其边界
// mLineChart.fitScreen();
/* y轴是否自动缩放;当缩放时,y轴的显示会自动根据x轴范围内数据的最大最小值而调整。财务报表比较有用,默认false*/
mLineChart.setAutoScaleMinMaxEnabled(true);
mLineChart.setExtraLeftOffset(10f); // 这个与上面的区别是不会忽略其自己计算的偏移。
// mLineChart.setDoubleTapToZoomEnabled(false);//双击屏幕缩放
// mLineChart.setScaleEnabled(false);
// mLineChart.setScaleXEnabled(true);
// mLineChart.setScaleYEnabled(false);
/* var axisLeft = mLineChart.getAxisLeft()
// axisLeft.axisMinimum = 45f
//如果设置为true那么下面方法设置最小间隔生效,默认为false
axisLeft.setGranularityEnabled(true);
//设置Y轴的值之间的最小间隔。这可以用来避免价值复制当放大到一个地步,小数设置轴不再数允许区分两轴线之间的值。
axisLeft.setGranularity(10f);
binding.viewModel?.requestGrowthData("1")*/
initHeightWidth(true)
//初始化
fun initHeightWidth(isHeight: Boolean) {
binding.tvTopTip.text = if (isHeight) "身高(cm)" else "体重(kg)"
mLineChart.setExtraLeftOffset(if (isHeight) 10f else 5f); // 这个与上面的区别是不会忽略其自己计算的偏移。
var axisLeft = mLineChart.getAxisLeft()
//如果设置为true那么下面方法设置最小间隔生效,默认为false
axisLeft.setGranularityEnabled(true)
axisLeft.setGranularity(if (isHeight) 10f else 1f)
axisLeft.axisMinimum = if (isHeight) 45f else 1f
var xAxis = mLineChart.xAxis
/*设置X轴的位置(默认在上方)*/
xAxis.position = XAxis.XAxisPosition.BOTTOM
/*设置X轴坐标之间的最小间隔*/
xAxis.granularity = 1f
xAxis.mAxisMaximum = 72f
/*设置X轴值为字符串*/
xAxis.setValueFormatter(object : IAxisValueFormatter {
override fun getFormattedValue(value: Float, axis: AxisBase?): String {
return PinyinUtils.getFormatXLabel(value.toInt())
}
})
binding.viewModel?.requestGrowthData(if (isHeight) "1" else "0")
}
//设置数据
fun setData(data: GrowthBean?) {
xList.clear()
//一个LineDataSet就是一条线
var entriesMax = mutableListOf()
var entriesMin = mutableListOf()
var entriesUser = mutableListOf()
data?.max?.forEach {
entriesMax.add(Entry(it.x, it.y))
}
data?.min?.forEach {
xList.add("${it.x}")
entriesMin.add(Entry(it.x, it.y))
}
data?.user?.forEach {
entriesUser.add(Entry(it.x, it.y))
}
var set97 = LineDataSet(entriesMax, "97%")
set97.setColor(Color.parseColor("#FF7449"));
set97.setLineWidth(2f);
set97.setFillAlpha(65);
set97.setHighLightColor(Color.rgb(244, 117, 117));
set97.setDrawCircleHole(false);
set97.setDrawCircles(false)
set97.setDrawValues(false)
set97.setDrawFilled(true)
set97.setFillColor(Color.parseColor("#FFC9C3"));
set97.label = "97%"
set97.setDrawHighlightIndicators(false)
var set3 = LineDataSet(entriesMin, "3%")
set3.setColor(Color.parseColor("#FF7449"))
set3.setLineWidth(2f)
set3.setDrawCircleHole(false)
set3.setDrawCircles(false)
set3.setDrawValues(false)
set3.label = "3%"
set3.setDrawHighlightIndicators(false)
Collections.sort(entriesUser, EntryXComparator())
var setUser = LineDataSet(entriesUser, "宝宝")
setUser.setColor(Color.parseColor("#FD5A7B"))
setUser.setLineWidth(2f)
setUser.setDrawCircleHole(false)
setUser.setDrawCircles(true)
setUser.setDrawValues(true)
setUser.setCircleRadius(5f)
setUser.setCircleColor(Color.parseColor("#FD5A7B"))
setUser.setDrawHighlightIndicators(false)
var lineData: LineData
if (entriesUser.size > 0) {
lineData = LineData(set97, set3, setUser)
} else {
lineData = LineData(set97, set3)
}
//设置数据
mLineChart.setData(lineData)
//设置一页最大显示个数为6,超出部分就滑动
val ratio = xList.size.toFloat() / 6.toFloat()
//显示的时候是按照多大的比率缩放显示,1f表示不放大缩小
mLineChart.zoom(ratio, 1f, 0f, 1f)
mLineChart.isScaleYEnabled = false
mLineChart.setNoDataText("暂无数据")
//可以设置一条警戒线,如下:
val ll = LimitLine(data!!.monthAge.toFloat(), "今日")
ll.lineColor = Color.parseColor("#FF7449")
ll.lineWidth = 1f
ll.enableDashedLine(10f, 10f, 0f);
ll.textColor = Color.parseColor("#FF5A7D")
ll.textSize = 12f
ll.setLabelPosition(LimitLine.LimitLabelPosition.RIGHT_BOTTOM)
mLineChart.xAxis.addLimitLine(ll)
//移到某个位置
mLineChart.moveViewTo(data!!.monthAge.toFloat() - 3, if (isHeight) 160f else 80f, YAxis.AxisDependency.RIGHT)
/*渲染区间背景*/
set97.setFillFormatter(MyFillFormatter(set3))
mLineChart.renderer = MyLineLegendRenderer(mLineChart, mLineChart.animator, mLineChart.viewPortHandler)
}
还有一个效果没有实现,就是滑动悬浮的线上的数。
渲染区间背景 是自定义的FillFormatter
public class MyFillFormatter implements IFillFormatter {
private ILineDataSet boundaryDataSet;
public MyFillFormatter() {
this(null);
}
//Pass the dataset of other line in the Constructor
public MyFillFormatter(ILineDataSet boundaryDataSet) {
this.boundaryDataSet = boundaryDataSet;
}
@Override
public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) {
return 0;
}
//Define a new method which is used in the LineChartRenderer
public List getFillLineBoundary() {
if(boundaryDataSet != null) {
return ((LineDataSet) boundaryDataSet).getValues();
}
return null;
}}
调用:
/*渲染区间背景*/
set97.setFillFormatter(MyFillFormatter(set3))
mLineChart.renderer = MyLineLegendRenderer(mLineChart, mLineChart.animator, mLineChart.viewPortHandler)
这里是在Stack Overflow里看到的
参考链接1
参考链接2
附上参考的文章
- Android图表MPandroidChart之曲线图绘制教程
- 效果1
- 效果2
- Android-LineChart显示多条曲线