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;

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容