技术无止境,只怕不学习啊,Flutter 我们开始吧
今天我们就按照这个步骤来实现下自定义View
CustomPainter的使用
新建类继承于CustomPainter并且实现CustomPainter里面的paint()和shouldRepaint方法。
class TestPainter extends CustomPainter {@overridevoid paint(Canvas canvas, Size size) {// TODO: implement paint}@overridebool shouldRepaint(CustomPainter oldDelegate) {// TODO: implement shouldRepaint///是否需要重绘return true;}}
paint方法就是Flutter中负责View绘制的地方,使用传递来的canvas和size即可完成对目标View的绘制。
shouldRepaint是控制自定义View是否需要重绘的,返回fals代表这个View在构建完成后不需要重绘。
canvas中有多个与绘制相关的方法,如drawLine()、drawRect()、drawOval()、drawOval()、等方法。
但是,仅仅使用canvas这个画布还不够,我们还需要一个画笔paint,我们使用如下代码来构建paint
Paint _paint = new Paint()..color = Colors.blueAccent//画笔颜色..strokeCap = StrokeCap.round//画笔笔触类型..isAntiAlias = true//是否启动抗锯齿..style=PaintingStyle.fill//绘画风格,默认为填充..blendMode=BlendMode.exclusion//颜色混合模式..colorFilter=ColorFilter.mode(Colors.blueAccent, BlendMode.exclusion)//颜色渲染模式,一般是矩阵效果来改变的,但 是flutter中只能使用颜色混合模式..maskFilter=MaskFilter.blur(BlurStyle.inner, 3.0)//模糊遮罩效果,flutter中只有这个..filterQuality=FilterQuality.high//颜色渲染模式的质量..strokeWidth = 10.0;//画笔的宽度
可以根据需要去具体的了解和使用
举个例子画一条线
class TestPainter extends CustomPainter {Paint _paint = new Paint()..color = Colors.blueAccent // 画笔颜色..strokeCap = StrokeCap.round //画笔笔触类型..isAntiAlias = true //是否启动抗锯齿..strokeWidth = 6.0; //画笔的宽度@overridevoid paint(Canvas canvas, Size size) {// TODO: implement paintcanvas.drawLine(Offset(20.0, 20.0), Offset(100.0, 100.0), _paint);}@overridebool shouldRepaint(CustomPainter oldDelegate) {// TODO: implement shouldRepaint///是否需要重绘return true;}}
设置画笔的颜色为蓝色,打开抗锯齿、设置笔触的类型为圆角并且设置画笔的宽度为6.0像素,
绘制了一条直线,从左边(20,20)的位置到坐标为(100,100)的位置,这样便完成了最简单的view绘制。
但是,现在我们并不能去运行这个程序,我们自定义的view从根本也不是一个Widget,所以也没法直接在Widget tree中去构建,所以这个时候就要借助与CustomPaint来给我们自定义的CustomPainter来做渲染。
CustomPaint就是继承于SingleChildRenderObjectWidget的一个Widget,使用时你只需要传入你自定义的CustomPainter即可,当然CustomPaint也可以传入自己的child widget来完成Widget的组合
代码:
void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.red, // 风格颜色visualDensity: VisualDensity.adaptivePlatformDensity,),routes:{"new_page":(context) => NewRoute(),},home: MyHomePage(title: '测试项目'),);}
}class MyHomePage extends StatefulWidget {MyHomePage({Key key, this.title}) : super(key: key);final String title;@override_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {@overrideWidget build(BuildContext context) {// TODO: implement buildvar paint = CustomPaint(size: Size(300,300),painter: TestPainter(),);return Scaffold(appBar: AppBar(title: Text( "Canvas的用法"),),body: Container(child: paint,),);}
}
效果图:
绘制点drawPoints
drawPoints(PointMode pointMode, List points, Paint paint)
绘制点非常的简单,需要传入PointMode枚举,坐标list和paint
PointMode的枚举类型有三个,points(点),lines(线,隔点连接),polygon(线,相邻连接)
代码:
@overridevoid paint(Canvas canvas, Size size) {// TODO: implement paintcanvas.drawPoints(PointMode.points,[Offset(20, 20),Offset(100.0, 100.0),Offset(100.0, 200.0),Offset(200.0, 200.0),Offset(200.0, 100.0),Offset(280.0, 20.0),Offset(20, 20),],_paint);}
上面定义了7个点,第一个和最后一个点重合。
设置PointMode为points看下效果。
把PointMode改为lines
@overridevoid paint(Canvas canvas, Size size) {// TODO: implement paintcanvas.drawPoints(PointMode.lines,[Offset(20, 20),Offset(100.0, 100.0),Offset(100.0, 200.0),Offset(200.0, 200.0),Offset(200.0, 100.0),Offset(280.0, 20.0),Offset(20, 20),],_paint);}
效果:
PointMode为lines时,两个点相互连接,也就是说第一个和第二个点连接,第三个跟第四个连接,如果最后只有一个点就舍弃不连接了,在我们的例子中有7个点,所以图中只有三条连线。
把PointMode改为polygon
@overridevoid paint(Canvas canvas, Size size) {// TODO: implement paintcanvas.drawPoints(PointMode.polygon,[Offset(20, 20),Offset(100.0, 100.0),Offset(100.0, 200.0),Offset(200.0, 200.0),Offset(200.0, 100.0),Offset(280.0, 20.0),Offset(20, 20),],_paint);}
效果:
绘制圆rawCircle
canvas.drawCircle(offset, radius, paint)
绘制圆很简单,仅仅圆心的坐标、半径和paint即可。
用法:
@overridevoid paint(Canvas canvas, Size size) {// TODO: implement paintcanvas.drawCircle(Offset(100, 100), 100, _paint);}
效果:
可以看到我们在坐标(100,100)的位置绘制了一个半径为的圆。
可以看到这个圆都被颜色填充了,明明我们在前面定义画笔的宽度为6来着,怎么回填充满呢?
其实是因为,paint的默认绘画风格为填充,尝试把style改为PaintingStyle.stroke。
Paint _paint = new Paint()..color = Colors.blueAccent // 画笔颜色..strokeCap = StrokeCap.round //画笔笔触类型..isAntiAlias = true //是否启动抗锯齿..strokeWidth = 6.0 //画笔的宽度..style = PaintingStyle.stroke; // 样式
再来看下效果:
绘制椭圆drawOval
drawOval(Rect rect, Paint paint)
绘制椭圆就相对简单很多,要传入Rect和paint,使用Rect便可确认这个矩形的大小和位置。
Rect也有多种构建方式:
fromPoints(Offset a, Offset b)
使用左上和右下角坐标来确定矩形的大小和位置
fromCircle({ Offset center, double radius })
使用圆的圆心点坐标和半径和确定外切矩形的大小和位置
fromLTRB(double left, double top, double right, double bottom)
使用矩形左边的X坐标、矩形顶部的Y坐标、矩形右边的X坐标、矩形底部的Y坐标来确定矩形的大小和位置
fromLTWH(double left, double top, double width, double height)
使用矩形左边的X坐标、矩形顶部的Y坐标矩形的宽高来确定矩形的大小和位置
这4种方式无论你使用那种都是一样的,都可以确定这个矩形的位置和大小,淡然这个椭圆也是在这个矩形之中内切的
@overridevoid paint(Canvas canvas, Size size) {// TODO: implement paintRect rect1= Rect.fromPoints(Offset(50.0, 50.0), Offset(130.0, 100.0));canvas.drawOval(rect1, _paint);}
效果:
宽度大于高度的椭圆
Rect rect2= Rect.fromPoints(Offset(50.0, 150.0), Offset(130.0, 300.0));
canvas.drawOval(rect2, _paint);
效果:
高度大于宽度的椭圆
Rect rect3= Rect.fromPoints(Offset(50.0, 320.0), Offset(130.0, 400.0));
canvas.drawOval(rect3, _paint);
效果:
高度等于宽度的,是圆
绘制圆弧drawArc
drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
绘制圆弧很简单,首先还是需要Rect来确认圆弧的位置,还需要开始的弧度、结束的弧度、是否使用中心点绘制、以及paint
弧度
根据定义,一周的弧度数为2πr/r=2π,360°角=2π弧度,因此,1弧度约为57.3°,即57°17’44.806’’,1°为π/180弧度,近似值为0.01745弧度,周角为2π弧度,平角(即180°角)为π弧度,直角为π/2弧度。
度 弧度
0° 0
30° π/6
45° π/4
60° π/3
90° π/2
120° 2π/3
180° π
270° 3π/2
360° 2π
例子:
@overridevoid paint(Canvas canvas, Size size) {// TODO: implement paintdouble PI = 3.1415926;var sweepAngle = PI / 2;var startAngle = 0.0;Rect rect = Rect.fromCircle(center: Offset(100.0, 100), radius: 80);canvas.drawArc(rect, startAngle, sweepAngle, false, _paint);}
定义π为3.1415926,定义开始的角度为0°扫过的角度为PI / 2(90°),设置userCenter为false
效果:
好像真的是90°啊,还是打开userCenter看下效果。
@overridevoid paint(Canvas canvas, Size size) {
// TODO: implement paintdouble PI = 3.1415926;var sweepAngle = PI / 2;var startAngle = 0.0;Rect rect = Rect.fromCircle(center: Offset(100.0, 100), radius: 80);canvas.drawArc(rect, startAngle, sweepAngle, true, _paint);}
确实是一个90°的圆弧。
绘制圆角矩形drawDRRect
drawRRect(RRect rrect, Paint paint)
其实使用起来也是非常的简单,使用RRect确定矩形大小及弧度,使用paint来完成绘制。
RRect构建起来非常的方便,直接使用fromRectAndRadius即可
RRect.fromRectAndRadius(rect, radius)
rect依然用来表示矩形的位置和大小,radius用来表示圆角的大小。
@overridevoid paint(Canvas canvas, Size size) {
// TODO: implement paintRect rect = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);RRect rRect = RRect.fromRectAndRadius(rect, Radius.circular(10.0));canvas.drawRRect(rRect, _paint);}
圆角度数为10
圆角度数等于Rect radius大小
@overridevoid paint(Canvas canvas, Size size) {
// TODO: implement paintRect rect = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);RRect rRect = RRect.fromRectAndRadius(rect, Radius.circular(60.0));canvas.drawRRect(rRect, _paint);}
效果:
绘制双圆角矩形drawRRect
drawDRRect(RRect outer, RRect inner, Paint paint)
和drawRRect类似,使用RRect确定内部、外部矩形大小及弧度,使用paint来完成绘制。
@overridevoid paint(Canvas canvas, Size size) {
// TODO: implement paintRect rect4 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);Rect rect5 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 40.0);RRect rRectOut = RRect.fromRectAndRadius(rect4, Radius.circular(10.0));RRect rRectInner = RRect.fromRectAndRadius(rect5, Radius.circular(10.0));canvas.drawDRRect(rRectOut, rRectInner, _paint);}
使用Rect.fromCircle来创建Rect,使用RRect.fromRectAndRadius来创建RRect
可以看到两个圆角矩形哦,淡然我们可以尝试调整角度的度数大小。
@overridevoid paint(Canvas canvas, Size size) {
// TODO: implement paintRect rect4 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);Rect rect5 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 40.0);RRect rRectOut = RRect.fromRectAndRadius(rect4, Radius.circular(40.0));RRect rRectInner = RRect.fromRectAndRadius(rect5, Radius.circular(30.0));canvas.drawDRRect(rRectOut, rRectInner, _paint);}
效果:
@overridevoid paint(Canvas canvas, Size size) {
// TODO: implement paintRect rect4 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);Rect rect5 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 40.0);RRect rRectOut = RRect.fromRectAndRadius(rect4, Radius.circular(60.0));RRect rRectInner = RRect.fromRectAndRadius(rect5, Radius.circular(40.0));canvas.drawDRRect(rRectOut, rRectInner, _paint);}
效果:
可以调整两个圆弧的位置来获得交叉的圆弧效果。
本篇文章就到这里,下一篇继续