Android 身高体重曲线的实现

场景

产品需求,需要增加一个身高体重曲线的模块,设计图如下
设计图.png
体重曲线也是这样。

准备

先来一张实现的粗略效果图
实现效果.gif

曲线图,折线图等,大多使用流行强大的MPandroidChart库,这里也是。
在网上找了一些类似的效果,都不是很符合设计图,
效果1.gif

效果2.gif
效果3.png

实现

直接附上代码,注释都很清楚。主要调用库的方法



        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<Entry>()
        var entriesMin = mutableListOf<Entry>()
        var entriesUser = mutableListOf<Entry>()
        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<Entry> 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显示多条曲线

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值