场景
产品需求,需要增加一个身高体重曲线的模块,设计图如下
准备
先来一张实现的粗略效果图
曲线图,折线图等,大多使用流行强大的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 ListgetFillLineBoundary() { if(boundaryDataSet != null) { return ((LineDataSet) boundaryDataSet).getValues(); } return null; }}复制代码
调用:
/*渲染区间背景*/ set97.setFillFormatter(MyFillFormatter(set3)) mLineChart.renderer = MyLineLegendRenderer(mLineChart, mLineChart.animator, mLineChart.viewPortHandler)复制代码
这里是在Stack Overflow里看到的
#最后 附上参考的文章