在本文中,将通过的Flutter的自定义view实现曲线统计图,来学习一些有关自定义view的相关知识,举一反三,更加熟练的运用自定义view.最终效果如图.
正文开始
基本运用
若想用自定义view,首先要先建一个类并继承 CustomPainter
class BaseLineChart extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// 绘制代码
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true; // [true]允许重新绘制
}
}
建好之后,放在CustomPaint中即可当普通Widget使用
Container(
child: CustomPaint(painter: BaseLineChart()),
)
绘制规则
在绘制方式上与Android原生的绘制规则基本相同,在屏幕坐标系中,以屏幕左上角作为原点,这个原点向右是X轴的正轴,向下是Y轴正轴。如下所示:
X和Y的最大值为手机屏幕的像素值,如:1080 × 1920.所以在绘制时会更多的采用比例的方式设置长宽.
开始绘制
初始化画笔
设置为成员变量
/// 画图的笔
Paint painter = Paint()
..strokeWidth = 1.2
..style = PaintingStyle.stroke;
/// 画文字的笔
TextPainter textPainter = TextPainter(
textDirection: TextDirection.ltr,
maxLines: 1,
);
设置区域,确定宽高
以父组件宽高为尺,确定出比例,规定绘制内容宽高,以确保不同手机屏幕显示的比例不变.
@override
void paint(Canvas canvas, Size size) {
// [size]为父组件的尺寸 ,
// 超过父组件的绘制内容都会被剪切掉
Rect rect = Offset.zero & size;
canvas.clipRect(rect);
// 设置背景颜色
painter.color = Colors.white;
painter.style = PaintingStyle.fill;
canvas.drawRect(rect, painter);
// 根据父组件尺寸,确定自定义view的宽,文字大小
width = size.width;
yTextwidth = width / 7;
unitWidth = (width - yTextwidth) / 4;
...
}
绘制背景横线
绘制横线时,按从左到右,从上到下的方式绘制.
@override
void paint(Canvas canvas, Size size) {
...
painter.strokeWidth = 1.2; // 线条宽度
painter.color = Color(0xffEBECF0); // 线段颜色
// [LINE_NUM] 线段数量
for (int i = 0; i < LINE_NUM - 1; i++) {
// 绘制横线
// drawLine('起点坐标','终点坐标')
canvas.drawLine(Offset(yTextwidth, i * LINE_SPACE + Y_TEXT_HEIGHT), Offset(width, i * LINE_SPACE + Y_TEXT_HEIGHT), painter);
}
painter.strokeWidth = 1.4; // 最后一条线条较粗
painter.color = Color(0xffC4C6CF);
// 绘制横线
canvas.drawLine(Offset(yTextwidth, (LINE_NUM - 1) * LINE_SPACE + Y_TEXT_HEIGHT),
Offset(width, (LINE_NUM - 1) * LINE_SPACE + Y_TEXT_HEIGHT), painter);
...
}
绘制文本,X,Y值及标题
绘制文本时调用textPainter.paint(canvas,'起始坐标值'),这个方法中只有开始位置坐标,坐标值为文本的左上角,所以绘制时要考虑到文本的宽度,与高度,保证文字显示位置正常.
@override
void paint(Canvas canvas, Size size) {
...
// y轴的标题
textPainter.text = TextSpan(
text: '文本内容',
style: new TextStyle(
color: Color(0xff303133),
fontSize: S(20),
),
);
textPainter.paint(canvas, Offset(0, 0));
// y 轴文字
for (int i = 0; i < LINE_NUM; i++) {
textPainter.text = TextSpan(
text: '${(LINE_NUM - i - 1) * 1000}', // 模拟数据
style: new TextStyle(
color: Colors.black,
fontSize: S(24),
),
);
// 调用此方法的目的为得到上面所输入的文字宽度与高度
textPainter.layout();
// 减去 textPainter.width / 2 ,textPainter.height / 2 目的为文字居中显示
textPainter.paint(canvas,
Offset((yTextwidth - textPainter.width) / 2, i * LINE_SPACE - textPainter.height / 2 + Y_TEXT_HEIGHT));
}
double xTextWidth = width - yTextwidth;
var temp = xTextWidth / 4;
// x 文字
for (int i = 1; i < 5; i++) {
textPainter.text = TextSpan(
text: '$i 月',
style: new TextStyle(
color: Colors.black,
fontSize: 12,
),
);
// 调用此方法的目的为得到上面所输入的文字宽度与高度
textPainter.layout();
// 减去 textPainter.width / 2 目的为文字居中显示
textPainter.paint(
canvas,getCurvePath(
Offset(yTextwidth + temp * i - temp / 2 - textPainter.width / 2,
(LINE_NUM - 1) * LINE_SPACE + 6 + Y_TEXT_HEIGHT));
}
...
}
绘制曲线
获取曲线坐标
在flutter中可以通过Path绘制线条,把已知的四个点连起来,就是基本的折线统计图,但要绘制出点与点之前平滑过渡的曲线,就需要另外一个方法得到曲线的坐标,这个方法就是cos函数.
做之前先回忆一下cos长啥样.
绘制所需要的就是0 ~ 2π 间的曲线.
/// 获取曲线路径
/// [v1] 第一个Y轴值 [v2] 第二个Y轴值
/// [index] X轴坐标位置
Path getCurvePath(double v1, double v2, int index, Path path) {
int clipNum = 30; // 一段曲线被分割绘制的数量,越大曲线越平滑.
double temp = 1 / clipNum; // 遍历运算用到的临时数值
bool isNegativeNumber; // 是否为负数
double diff = (v1 - v2).abs(); // 两点之间的差值
isNegativeNumber = (v1 - v2) < 0; // 判断正负
for (int i = 0; i < clipNum; i++) {
path.lineTo(
// x点坐标值,x轴不参与cos运算
getX(temp * i + index.toDouble()),
// y点坐标值
// 公式 y = cos(v) + 1 , isNegativeNumber 为true时,用到的是π~2π之间的曲线,为false时,用到的是0~π间的曲线.
// 通过公式运算之后再与实际大小做比相乘得出实际结果,添加到Path
getY((cos((isNegativeNumber ? pi : 0) + pi * temp * i) + 1) * diff / 2 + (isNegativeNumber ? v1 : v2)));
}
// 返回Path
return path;
}
/// 获取Y轴坐标
double getY(double value) => (MAX_VALUE - value) / MAX_VALUE * (LINE_NUM - 1) * LINE_SPACE + Y_TEXT_HEIGHT;
/// 获取X轴坐标
double getX(double index) => yTextwidth + unitWidth / 2 + index * unitWidth;
以上代码为得到曲线个点坐标的方法,接下来绘制曲线
绘制
@override
void paint(Canvas canvas, Size size) {
...
List<double> values = List();
for (int i = 0; i < 4; i++) {
values.add(700.0 * i + 1000); // 模拟数据
}
Path path = Path(); // 将曲线路径点加入到'Path'路径中
for (int i = 0; i < values.length - 1; i++) {
double v1 = values[i];
double v2 = values[i + 1];
if (i == 0) {
path.moveTo(getX(i.toDouble()), getY(v1));
}
path = getCurvePath(v1, v2, i, path); // 获取曲线路径
}
// 绘制路径
canvas.drawPath(path, painter);
path.close();
...
}
完成曲线统计图绘制.
最后
这就是Flutter自定义View的基本使用方法,其中还有许多细节未展示,详细内容请下载完整代码.
以上为全部内容,如有错误请指正.欢迎转载,评论.