? ? ? ? UI圖如下
? ? ? ? 通過Flutter中文網(wǎng)的電子書知道,F(xiàn)lutter要實現(xiàn)一個自定義控件,需要用到CustomPaint和CustomPainter。
? ?CustomPaint當中有一個參數(shù)painter
這個painter就是CustomPainter
在CustomPaint中還有一個child的Widget參數(shù),這個應該都很熟悉,就是一個子控件,這個就可以用來設置自定義控件的大小尺寸。
? ? ? ? 而CustomPainter通過源碼可以知道,里面的參數(shù)并不能實現(xiàn)這個UI效果圖的計算和傳參,那么就需要自己去生成一個CustomChartPainter繼承于CustomPainter,并重載shouldRepaint、shouldRebuildSemantics和paint方法,其中shouldRepaint和shouldRebuildSemantics設置分別返回true和false。
根據(jù)UI圖呢,不需要x軸和y軸,但是有坐標顯示,那么我們需要兩個集合xAxis和yAxis,還需要展示的集合數(shù)據(jù)datas,單個柱形的寬度barWidth,y軸的刻度的個數(shù)num,那我們也把y軸刻度之間的間隔傳進來gap
分別通過size.width和size.height來獲取畫布的寬、高,那么y軸刻度之間的間隔就應該是size.height/num,通過這幾個值,就可以來計算出y軸刻度的繪制位置
首先先計算出各個刻度到畫布頂部的距離,(itemH*index)是刻度的高度,距離頂部的高度的話,就應該用畫布的高度減去刻度的高度 ??
final double _top=sizeH-(itemH*index);
刻度繪制的位置應該在y軸的左側(cè), 并且我想讓相對應的x軸的水平位置在刻度text的中間位置,那么就需要做一個偏移量的計算
final Offset textOffset =Offset(
????0 -ScreenUtil().setSp(12),
? _top -ScreenUtil().setSp(12) /2,
);
繪制文字,我們用TextPainter來實現(xiàn)
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軸最左側(cè)不按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圖,有個底色的柱狀圖,然后是有一個標識數(shù)量的藍色的柱狀圖,那么通過data * sh / (gap *num)來計算出柱狀圖的高度,?i *barWidth + (i * barGap) + barGap /2來計算出柱狀圖的左側(cè)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 數(shù)組
? ? ? ? ? ? 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;
}