前言
课程目标
- 了解并掌握Flutter自定义view的原理
- 掌握Flutter自定义view的方式与具体操作流程
- 利用本章节所学知识完成一个自动变化颜色的圆形自定义view
自动变化颜色的圆形自定义view效果图
1.Flutter自定义view原理
类比分析
1.1 画笔:Paint
Paint就是我们用来绘制自定义view的基础,我们可以通过给paint设置配置paint的属性来绘制不同样式的内容。
常用的属性有
- color(设置画笔颜色)
- strokeCap(画笔笔触类型)
- colorFilter(颜色渲染模式,一般是矩阵效果来改变的,但是flutter中只能使用颜色混合模式)
- style(描边还是填充对应于PaintingStyle.stroke及PaintingStyle.fill)
- strokeWidth(画笔的宽度)
- isAntiAlias(是否抗锯齿)
- shader(绘制渐变色常用的LinearGradient线性渐变,SweepGradient扫描渐变,RadialGradient辐射式渐变)
其他属性我就不逐一罗列了,感兴趣的读者可以结合代码自己写代码验证一下效果
1.2 画布:Canvas
有了第一步的画笔,那么下边我们就可以利用在第一步我们已经配置好的画笔在canvas上绘制具体的图形了,在Flutter中官方已经给我们提供好了一些列的绘制方法,如drawCircle绘制圆,drawRect绘制矩形,drawPath绘制任意路径。在canvas里给我们提供了大部分常用的绘制方法,方法前面均为canvas.drawxxx(xxx,paint)所示)这里的xxx其实就是下图中的内容:
canvas不仅仅提供大量绘制相关的方法,还提供了我们常用的平移canvas.translate,旋转canvas.rotate,裁剪canvas.clipRect等方法。
注:在整个canvas绘制的过程中系统坐标系是在左上角,向右及向下分别为x及y轴正向
1.3 绘制具体内容
在准备好了画笔(paint)跟画布(canvas)之后,下面我们只需在自定义View的
paint
方法中实现具体的逻辑来完成相关绘制;如下代码利用path绘制三角形跟利用drawCircle绘制圆的示例代码如下所示:
@override
void paint(Canvas canvas, Size size) {
//利用path绘制三角形
Path path = Path();
path.lineTo(100, 0);
path.lineTo(0, 100);
path.close();
canvas.drawPath(path, _paint);
//利用drawCircle圆心坐标点为(100, 100),半径为50的实心圆
// canvas.drawCircle(Offset(100, 100), 50, _paint);
}
三角形
圆心坐标点为(100,100),半径为100的实心圆
1.4 处理视图是否需要刷新
在自定义view时,如果我们绘制的view不需要改变,或者说图像绘制成功之后在它存在的整个生命周期中都不再需要改变,这个时候我们只需要在shouldRepaint
方法中直接返回false
即可,表示当前视图不需要刷新处理。
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
反之则需要根据业务场景,具体去改变shouldRepaint
的返回值,来通知视图是否需要刷新。
完整代码如下;
import 'package:flutter/material.dart';
/**
* desc:自定义view
* author: xiedong
* date: 2021/9/2
**/
void main() {
runApp(MaterialApp(
home: CustomTriangleView(),
));
}
class CustomTriangleView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("自定义VIEW"),
),
body: CustomPaint(
painter: TriangleView(),
),
);
}
}
class TriangleView extends CustomPainter {
var _paint;
TriangleView() {
_paint = Paint()
..color = Colors.blueAccent //画笔颜色
..strokeCap = StrokeCap.round //画笔笔触类型
..isAntiAlias = true //是否启动抗锯齿
..blendMode = BlendMode.exclusion //颜色混合模式
..style = PaintingStyle.fill //绘画风格,默认为填充
..colorFilter = ColorFilter.mode(Colors.blueAccent,
BlendMode.exclusion) //颜色渲染模式,一般是矩阵效果来改变的,但是flutter中只能使用颜色混合模式
..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果,flutter中只有这个
..filterQuality = FilterQuality.high //颜色渲染模式的质量
..strokeWidth = 15.0; //画笔的宽度
}
@override
void paint(Canvas canvas, Size size) {
//利用path绘制三角形
// Path path = Path();
// path.lineTo(100, 0);
// path.lineTo(0, 100);
// path.close();
// canvas.drawPath(path, _paint);
//利用drawCircle圆心坐标点为(100, 100),半径为50的实心圆
canvas.drawCircle(Offset(100, 100), 50, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
2.绘制颜色自动变化的圆
在开篇的时候我们给本章节提出的课程要求里提到利用所学的知识点绘制一个颜色可以自动变化的圆。那么基于此,我们首先确定自定义VIEW的状态需要动态改变,所以shouldRepaint
返回值应该为true
2.1. 设置shouldRepaint返回值为true
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
绘制函数不变,在绘制函数中,我们还是利用上述第一部分中绘制圆的逻辑代码来完成,需要改变的是,我们需要借助state来动态改变paint的颜色值。
2.2 绘制函数如下:
@override
void paint(Canvas canvas, Size size) {
//利用path绘制三角形
// Path path = Path();
// path.lineTo(100, 0);
// path.lineTo(0, 100);
// path.close();
// canvas.drawPath(path, _paint);
//利用drawCircle圆心坐标点为(100, 100),半径为50的实心圆
print('----------图像被重绘制');
canvas.drawCircle(Offset(100, 100), 50, _paint);
}
2.3 利用Flutter周期函数动态修改传入自定义View中的颜色值
@override
void initState() {
super.initState();
int count = 0;
var period = Duration(seconds: 1);
// print('currentTime='+DateTime.now().toString());
Timer.periodic(period, (timer) {
print('----颜色值改变---');
this.setState(() {
_color = _colorArr[Random().nextInt(4)];
});
});
}
2.4 在自定义View的构造方法中接收通过state传递过来的颜色值
在自定义view的构造方法中初始化paint颜色值由第一部分的固定值,改完通过从state传递回来的可变值,进而通过state的变化来刷新整个view的视图。
AutoChangeColorCircle(_color) {
_paint = Paint()
..color = _color //画笔颜色
..strokeCap = StrokeCap.round //画笔笔触类型
..isAntiAlias = true //是否启动抗锯齿
..blendMode = BlendMode.exclusion //颜色混合模式
..style = PaintingStyle.fill //绘画风格,默认为填充
..colorFilter = ColorFilter.mode(Colors.blueAccent,
BlendMode.exclusion) //颜色渲染模式,一般是矩阵效果来改变的,但是flutter中只能使用颜色混合模式
..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果,flutter中只有这个
..filterQuality = FilterQuality.high //颜色渲染模式的质量
..strokeWidth = 15.0; //画笔的宽度
}
效果如下
完整代码如下:
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
/**
* desc:自定义view
* author: xiedong
* date: 2021/9/2
**/
void main() {
runApp(MaterialApp(
// home: CustomTriangleView(),
home: AutoChangeColorCircleView(),
));
}
class AutoChangeColorCircleView extends StatefulWidget {
@override
State<StatefulWidget> createState() => ViewState();
}
class ViewState extends State<AutoChangeColorCircleView> {
var _colorArr = [
Colors.amberAccent,
Colors.blue,
Colors.deepOrange,
Colors.cyan,
Colors.black,
Colors.deepPurple,
];
var _color;
@override
void initState() {
super.initState();
int count = 0;
var period = Duration(seconds: 1);
// print('currentTime='+DateTime.now().toString());
Timer.periodic(period, (timer) {
print('----颜色值改变---');
this.setState(() {
_color = _colorArr[Random().nextInt(4)];
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("自定义VIEW"),
),
body: CustomPaint(
painter: AutoChangeColorCircle(_color),
),
);
}
}
class AutoChangeColorCircle extends CustomPainter {
var _paint;
AutoChangeColorCircle(_color) {
_paint = Paint()
..color = _color //画笔颜色
..strokeCap = StrokeCap.round //画笔笔触类型
..isAntiAlias = true //是否启动抗锯齿
..blendMode = BlendMode.exclusion //颜色混合模式
..style = PaintingStyle.fill //绘画风格,默认为填充
..colorFilter = ColorFilter.mode(Colors.blueAccent,
BlendMode.exclusion) //颜色渲染模式,一般是矩阵效果来改变的,但是flutter中只能使用颜色混合模式
..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果,flutter中只有这个
..filterQuality = FilterQuality.high //颜色渲染模式的质量
..strokeWidth = 15.0; //画笔的宽度
}
@override
void paint(Canvas canvas, Size size) {
//利用path绘制三角形
// Path path = Path();
// path.lineTo(100, 0);
// path.lineTo(0, 100);
// path.close();
// canvas.drawPath(path, _paint);
//利用drawCircle圆心坐标点为(100, 100),半径为50的实心圆
print('----------图像被重绘制');
canvas.drawCircle(Offset(100, 100), 50, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
完整代码详见Github Flutter入门进阶之旅专栏代码
在Flutter中除了继承CustomPainter重新绘制图形来完成自定义view,在某些简单的场景下,我们也可以利用组合Flutter现有的组件来完成自定义view,比如在上一篇博文中我们讲到的 Flutter入门进阶之旅 - Flutter课程表View就是通过组合Flutter中现有的组件来完成自定义view,感兴趣的读者可以从我的专栏中找到该文章,对比一下通过两种不同的方式来完成自定义Flutter的优缺点。