美文网首页
Flutter自定义控件--柱状图和计算

Flutter自定义控件--柱状图和计算

作者: 行走世间全都是妖怪 | 来源:发表于2020-12-25 11:15 被阅读0次

            UI图如下

            通过Flutter中文网的电子书知道,Flutter要实现一个自定义控件,需要用到CustomPaint和CustomPainter。

       CustomPaint当中有一个参数painter

    这个painter就是CustomPainter

    在CustomPaint中还有一个child的Widget参数,这个应该都很熟悉,就是一个子控件,这个就可以用来设置自定义控件的大小尺寸。

            而CustomPainter通过源码可以知道,里面的参数并不能实现这个UI效果图的计算和传参,那么就需要自己去生成一个CustomChartPainter继承于CustomPainter,并重载shouldRepaint、shouldRebuildSemantics和paint方法,其中shouldRepaint和shouldRebuildSemantics设置分别返回true和false。

    根据UI图呢,不需要x轴和y轴,但是有坐标显示,那么我们需要两个集合xAxis和yAxis,还需要展示的集合数据datas,单个柱形的宽度barWidth,y轴的刻度的个数num,那我们也把y轴刻度之间的间隔传进来gap

    分别通过size.width和size.height来获取画布的宽、高,那么y轴刻度之间的间隔就应该是size.height/num,通过这几个值,就可以来计算出y轴刻度的绘制位置

    首先先计算出各个刻度到画布顶部的距离,(itemH*index)是刻度的高度,距离顶部的高度的话,就应该用画布的高度减去刻度的高度   

    final double _top=sizeH-(itemH*index);

    刻度绘制的位置应该在y轴的左侧, 并且我想让相对应的x轴的水平位置在刻度text的中间位置,那么就需要做一个偏移量的计算

    final Offset textOffset =Offset(

        0 -ScreenUtil().setSp(12),

      _top -ScreenUtil().setSp(12) /2,

    );

    绘制文字,我们用TextPainter来实现

    TextPainter(

        text:TextSpan(

        text: value,

            style:TextStyle(

                fontSize:ScreenUtil().setSp(12),

                color: ColorUtils.getColor("#C2C2C2")),

          ),    

          textAlign: TextAlign.right,

          textDirection: TextDirection.ltr,

          textWidthBasis: TextWidthBasis.longestLine,

     )

     ..layout(minWidth:0, maxWidth:ScreenUtil().scaleWidth *40)

    ..paint(canvas, textOffset);

    这样,y轴刻度这边就绘制完成了。

    接下来绘制x轴和柱状图:先定义一个Paint,

    final paint =Paint()..style = PaintingStyle.fill;

    计算出x轴刻度之间的距离(为什么是length-1,是因为x轴最左侧不按0刻度来做)

    final itemW = size.width / (xAxis.length -1);

    按y轴相同的方式把x轴绘制出来(高度+12的原因就是x刻度标识要在x轴下方)

    for (var i =0; i < xAxis.length; i++) {

        final xData =xAxis[i];

         final xOffset =Offset(itemW * i, sh +12);

      // 绘制横轴标识

      TextPainter(

            textAlign: TextAlign.center,

            text:TextSpan(

            text:'$xData',

              style:TextStyle(

            fontSize:ScreenUtil().setSp(14), color: ColorUtils.getColor("#C2C2C2")),

            ),

            textDirection: TextDirection.ltr,

      )

    ..layout(

        minWidth:0,

          maxWidth: size.width,

        )

    ..paint(canvas, xOffset);

    再把柱状图之间的间隔计算出来

    final barGap = (size.width -barWidth *xAxis.length) / (xAxis.length -1);

    按照UI图,有个底色的柱状图,然后是有一个标识数量的蓝色的柱状图,那么通过data * sh / (gap *num)来计算出柱状图的高度, i *barWidth + (i * barGap) + barGap /2来计算出柱状图的左侧x坐标

    for (int i =0; i < datas.length; i++) {

        final double data =datas[i];

      final double top = sh - data * sh / (gap *num);

      final double left = i *barWidth + (i * barGap) + barGap /2;

      paint.color = ColorUtils.getColor("#EEEEEE");

      final allRect =Rect.fromLTWH(left, 0, barWidth, sh);//这个就是用来绘制灰度底色的全高的柱状图

      canvas.drawRect(allRect, paint);

      paint.color = ColorUtils.getColor("#305FFF");

      final rect =Rect.fromLTWH(left, top, barWidth, data * sh / (gap *num));

      canvas.drawRect(rect, paint);

    }

    到这里基本上整个柱状图的计算代码就完成了(没有x、y轴)。

    使用起来就方便很多了,放在CustomPaint中使用

    全部代码:同时也欢迎指正和更好的方法。

    import 'package:app_nh/utils/ColorUtils.dart';

    import 'package:flutter/cupertino.dart';

    import 'package:flutter_screenutil/flutter_screenutil.dart';

    class CustomChartextends StatefulWidget {

    final Listdatas;

      final ListxAxis;

      final ListyAxis;

      CustomChart(this.datas, this.yAxis, this.xAxis);

      @override

      CustomChartStatecreateState() =>CustomChartState();

    }

    class CustomChartStateextends State {

    @override

      Widgetbuild(BuildContext context) {

    ScreenUtil.init(context, width:375, height:667);

        return Column(

    mainAxisAlignment: MainAxisAlignment.center,

          children: [

    SizedBox(height:24 *ScreenUtil().scaleHeight),

            CustomPaint(

    painter:CustomChartPainter(

    // 最后向 ColumnChartPainter 传入 _animations 数组

                datas:widget.datas,

                xAxis:widget.xAxis,

                barWidth:ScreenUtil().scaleWidth *6.93,

                gap:50.0,

                yAxis:widget.yAxis,

                num:4,

              ),

              child:Container(

    width:ScreenUtil().scaleWidth *300,

                  height:ScreenUtil().scaleHeight *160),

            ),

            SizedBox(height:24 *ScreenUtil().scaleHeight),

          ],

        );

      }

    }

    class CustomChartPainterextends CustomPainter {

    final Listdatas;

      final ListxAxis;

      final ListyAxis;

      final barWidth;

      final gap;

      final num;

      CustomChartPainter(

    {@required this.datas,

          @required this.xAxis,

          @required this.yAxis,

          @required this.gap,

          @required this.barWidth,

          @required this.num});

      @override

      void paint(Canvas canvas, Size size) {

    _drawLabels(canvas, size);

        _drawBarChart(canvas, size);

      }

    void _drawLabels(Canvas canvas, Size size) {

    final double sizeH = size.height;

        final double sizeW = size.width;

        final double itemH = sizeH /num;

        yAxis.asMap().forEach((index, value) {

    final double _top = sizeH - (itemH * index);

          final Offset textOffset =Offset(

    0 -ScreenUtil().setSp(12),

            _top -ScreenUtil().setSp(12) /2,

          );

          TextPainter(

    text:TextSpan(

    text: value,

              style:TextStyle(

    fontSize:ScreenUtil().setSp(12),

                  color: ColorUtils.getColor("#C2C2C2")),

            ),

            textAlign: TextAlign.right,

            textDirection: TextDirection.ltr,

            textWidthBasis: TextWidthBasis.longestLine,

          )

    ..layout(minWidth:0, maxWidth:ScreenUtil().scaleWidth *40)

    ..paint(canvas, textOffset);

        });

      }

    void _drawBarChart(Canvas canvas, Size size) {

    final sh = size.height;

        final paint =Paint()..style = PaintingStyle.fill;

        final itemW = size.width / (xAxis.length -1);

        for (var i =0; i

    final xData =xAxis[i];

          final xOffset =Offset(itemW * i, sh +12);

          // 绘制横轴标识

          TextPainter(

    textAlign: TextAlign.center,

            text:TextSpan(

    text:'$xData',

              style:TextStyle(

    fontSize:ScreenUtil().setSp(14),

                  color: ColorUtils.getColor("#C2C2C2")),

            ),

            textDirection: TextDirection.ltr,

          )

    ..layout(

    minWidth:0,

              maxWidth: size.width,

            )

    ..paint(canvas, xOffset);

        }

    final barGap = (size.width -barWidth *xAxis.length) / (xAxis.length -1);

        for (int i =0; i

    final double data =datas[i];

          final double top = sh - data * sh / (gap *num);

          final double left = i *barWidth + (i * barGap) + barGap /2;

          paint.color = ColorUtils.getColor("#EEEEEE");

          final allRect =Rect.fromLTWH(left, 0, barWidth, sh);

          canvas.drawRect(allRect, paint);

          paint.color = ColorUtils.getColor("#305FFF");

          final rect =Rect.fromLTWH(left, top, barWidth, data * sh / (gap *num));

          canvas.drawRect(rect, paint);

        }

    }

    @override

      boolshouldRepaint(CustomPainter oldDelegate) =>true;

      @override

      boolshouldRebuildSemantics(CustomPainter oldDelegate) =>false;

    }

    相关文章

      网友评论

          本文标题:Flutter自定义控件--柱状图和计算

          本文链接:https://www.haomeiwen.com/subject/vrlzgktx.html