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

        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自定义控件--柱状图和计算)