CustomPainter

Flutter 中实现绘制的主要是CustomPainter类、

我们一般继承这个类,来使用它;

  1. class MyPainter extends CustomPainter{
  2. @override
  3. void paint(Canvas canvas, Size size) {
  4. }
  5. @override
  6. bool shouldRepaint(CustomPainter oldDelegate) {
  7. return null;
  8. }
  9. }复制代码

然后放在父控件的child里用CustomPaint包裹

  1. child: new CustomPaint(
  2. size: new Size(200,200),
  3. painter: new MyPainter())复制代码

故事的开始:paint()

自定义绘制非常简单,创建好 Paint 对象,重写 paint(),绘制代码放在paint()里面,大概就是这样:

  1. Paint _paint = Paint()
  2. ..color = Colors.amber //画笔颜色
  3. ..strokeCap = StrokeCap.round //画笔笔触类型
  4. ..isAntiAlias = true //是否启动抗锯齿
  5. ..strokeWidth = 15.0; //画笔的宽度
  6. @override
  7. void paint(Canvas canvas, Size size) {
  8. // 画个实心圆
  9. canvas.drawCircle(new Offset(200, 200), 100, _paint);
  10. }复制代码

Canvas和Paint基础

Canvas.drawXXX() 系列方法和 Paint 的基础掌握了,就能够进行简单的绘制需求。

  1. Canvas 类下的所有 draw- 开头的方法,例如 drawCircle() drawArc()
  2. Paint 的初始化
    1. Paint _paint = Paint()
    2. ..color = Colors.deepOrange//画笔颜色
    3. ..strokeCap = StrokeCap.round //画笔笔头类型
    4. ..isAntiAlias = true //是否开启抗锯齿
    5. ..blendMode = BlendMode.src//颜色混合模式
    6. ..style = PaintingStyle.fill //画笔样式,默认为填充
    7. ..colorFilter = ColorFilter.mode(Colors.blueAccent,
    8. BlendMode.src) //颜色渲染模式
    9. ..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果
    10. ..filterQuality = FilterQuality.high //颜色渲染模式的质量
    11. ..strokeWidth = 5.0; //画笔的宽度复制代码

好了,基础介绍完了,大家可以直接点击下方,打开Flutter官方文档查看原汁原味的资料哦

这篇文章到这里就可以关闭了,嘻嘻。

以下结合示例说说主要方法的使用

填充颜色 - drawColor(Color color, BlendMode blendMode)

这个方法一般用于画板底色填充及蒙版(使用蒙版时要注意图片混合模式设为BlendMode.srcOver

  1. canvas.drawColor(Color.fromARGB(80, 255, 0, 0), BlendMode.srcOver);复制代码

画圆 - drawCircle(Offset c, double radius, Paint paint)

第一个参数 c 是圆心点的坐标,直接new一个Offset出来填入x,y坐标就完事了,第二个参 radius 是圆的半径,paint不必多说;

  1. canvas.drawCircle(new Offset(200, 200), 100, _paint);复制代码

坐标系是以控件左上角开始的,跟初中学的坐标系可不一样;

画矩形 - drawRect(Rect rect, Paint paint)

  1. canvas.drawRect(new Rect.fromLTWH(10, 50, 50, 50),_paint);
  2. _paint.style = PaintingStyle.stroke; // 中途将画笔风格设为环形
  3. canvas.drawRect(new Rect.fromLTWH(120, 50, 50, 50),_paint);复制代码

效果如下:

画点 - drawPoints(PointMode pointMode, List points, Paint paint)

第一个参数是点的模式,分三种

  • PointMode.points
  • PointMode.lines
  • PointMode.polygon

第二个参数是一个点的集合,第三个参数...emmm,好了话不多说,上图

PointMode.points - 点模式

  1. List<Offset> points = new List();
  2. points.add(new Offset(100, 100));
  3. points.add(new Offset(125, 200));
  4. points.add(new Offset(50, 150));
  5. points.add(new Offset(150, 150));
  6. points.add(new Offset(75, 200));
  7. canvas.drawPoints(PointMode.points, points, _paint);复制代码

PointMode.lines - 情侣模式

  1. canvas.drawPoints(PointMode.points, points, _paint);复制代码

咦,我左下角的点呢?哦,原来这个模式没有配对的小点点会被删除,呜呜呜,单身狗没人权的嘛?

PointMode.polygon - 连线模式

  1. canvas.drawPoints(PointMode.polygon, points, _paint);复制代码

画椭圆 - drawOval(Rect rect, Paint paint)

这个方法和drawRect()是一毛一样的,只不过一个画的是矩形,一个是椭圆

  1. canvas.drawOval(new Rect.fromLTWH(50, 50, 100, 50), _paint);
  2. _paint.style = PaintingStyle.stroke;
  3. canvas.drawOval(new Rect.fromLTWH(170, 50, 100, 50), _paint);复制代码

画一条直线 - drawLine(Offset p1, Offset p2, Paint paint)

p1线的起点,p2线的终点

  1. canvas.drawLine(new Offset(100, 100), new Offset(200, 200), _paint);复制代码

Flutter并没有提供画多条线的方法,只能多写几次 drawLine() 或者使用 drawPoints() 的情侣模式

画圆角矩形 - drawRRect(RRect rrect, Paint paint)

  1. canvas.drawRRect(new RRect.fromLTRBR(50, 50, 200, 100, new Radius.circular(10.0)), _paint);
  2. canvas.drawRRect(new RRect.fromLTRBR(50, 150, 200, 250, new Radius.elliptical(10.0, 30.0)), _paint);复制代码

下面这个矩形看似纵向拉伸过了,其实没有,只是圆角模式是 elliptical (椭圆)

画矩形环 - drawDRRect(RRect outer, RRect inner, Paint paint)

第一个参数的区域必须包括第二个参数的区域,否则无法显示;

  1. canvas.drawDRRect(new RRect.fromLTRBR(50, 50, 200, 100, new Radius.circular(10.0)),
  2. new RRect.fromLTRBR(60, 60, 190, 90, new Radius.circular(10.0)), _paint);复制代码

画圆弧或扇形 - drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)

第一个参数仍然是一块矩形区域,第二个参数 startAngle 与第三个参数 sweepAngle 需要注意是弧度制,要转化一下  乘以个  (pi / 180.0) 就行了度(下图中红线是 0 度的位置;顺时针为正角度,逆时针为负角度),第四个参数 useCenter 代表是否与圆心连接,

  1. canvas.drawArc(new Rect.fromLTWH(50, 50, 100, 50), 0.0 * (pi / 180.0), 90 * (pi / 180.0), false, _paint);
  2. canvas.drawArc(new Rect.fromLTWH(50, 50, 100, 50), 200.0* (pi / 180.0), 90 * (pi / 180.0), true, _paint);
  3. _paint.style = PaintingStyle.stroke; // 画线模式
  4. canvas.drawArc(new Rect.fromLTWH(50, 50, 100, 50), 100.0* (pi / 180.0), 90 * (pi / 180.0), false, _paint);复制代码

以上就是 Canvas 所有的简单图形的绘制。除了简单图形的绘制, Canvas 还可以使用drawPath(Path path, Paint paint)来绘制自定义图形。

画路径 - drawPath(Path path, Paint paint)

这个方法通过描述路径的方式来绘制图形,用法大概是这样:

  1. Path _path = Path();
  2. @override
  3. void paint(Canvas canvas, Size size) {
  4. _paint.style = PaintingStyle.stroke; // 画线模式
  5. _path.addArc(new Rect.fromLTWH(50, 50, 50, 50), 135.0 * (pi / 180.0), 225.0 * (pi / 180.0));
  6. _path.addArc(new Rect.fromLTWH(100, 50, 50, 50), 180.0 * (pi / 180.0), 225.0 * (pi / 180.0));
  7. _path.lineTo(100, 140);
  8. _path.lineTo(58, 93);
  9. canvas.drawPath(_path, _paint);
  10. }复制代码

Path 主要有方法如下:

直接描述路径的方法还可以细分为两组:添加子图形和画线(直线或曲线)

  • addXXX() - 添加子图形(由于此类方法参数与上面介绍的画简单图形一样,就不多赘述了)
  1. addArc(Rect oval, double startAngle, double sweepAngle) - 添加圆弧
  2. addOval(Rect oval) - 添加圆
  3. addPolygon(List<Offset> points, bool close) - 添加一个由点的集合描述的多边形
  4. addRect(Rect rect) - 添加矩形
  5. addRRect(Rect rect) - 添加圆角矩形
  6. addPath(Path path, Offset offset) - 添加子路径
  • XXXTo() - 画线(直线或曲线)

向目标位置画直线 - lineTo(double x, double y) / relativelineTo(double x, double y)

当前位置向目标位置画一条直线, x 和 y 是目标位置的坐标。这两个方法的区 别是, lineTo(x, y) 的参数是绝对坐标,而 relativeLineTo(x, y) 的参数是相对当前位置相对坐标 ;

  1. _paint.style = PaintingStyle.stroke; // 画线模式
  2. _path.lineTo(100, 100); // 由当前位置 (0, 0) 向 (100, 100) 画一条直线
  3. _path.relativeLineTo(100, 0); // 由当前位置 (100, 100) 向正右方画100像素的位置
  4. canvas.drawPath(_path, _paint);复制代码

画二阶贝塞尔曲线 - quadraticBezierTo(double x1, double y1, double x2, double y2) /  relativeQuadraticBezierTo(double x1, double y1, double x2, double y2)

x1,y1是控制点的坐标;x2,y2是结束点的坐标;relativeQuadraticBezierTo()同上面相对直线方法

  1. _paint.style = PaintingStyle.stroke; // 画线模式
  2. List<Offset> points = new List();
  3. points.add(new Offset(100, 50)); // 画出控制点位置,方便理解
  4. canvas.drawPoints(PointMode.points, points, _paint);
  5. _path.moveTo(0, 100); // 移动起点到(0,100)
  6. _path.quadraticBezierTo(100, 50, 200, 100);
  7. canvas.drawPath(_path, _paint);复制代码

画三阶贝塞尔曲线 - cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) / relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3)

和上面这个 quadraticBezierTo()和relativeQuadraticBezierTo() 的二阶贝塞尔曲线同理,就不多说了。

移动到某点 - moveTo(double x, double y) / relativeMoveTo(double dx, double dy)

不论是直线还是贝塞尔曲线,都是以当前位置作为起点,而不能指定起点。但可以通过 moveTo(x, y) 或 relativeMoveTo(x, y) 来改变当前位置,从而间接地设置这些方法的起点。

  1. _paint.style = PaintingStyle.stroke; // 画线模式
  2. _path.moveTo(20, 40); // 移动起点到(20,40)
  3. _path.lineTo(80, 100); // 画条斜线
  4. _path.moveTo(100, 40); // 移动起点到(100,20)
  5. _path.lineTo(100, 100); // 画条直线
  6. canvas.drawPath(_path, _paint);复制代码

但凡事都有例外 arcTo() 这个方法并不从当前位置开始绘制

画弧线 - arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo)

前三个参数,我们已经很熟悉了,最后一个参数的意思是,画这个弧的时候是拖着笔到起点还是抬下笔到起点

  1. _paint.style = PaintingStyle.stroke; // 画线模式
  2. _path.moveTo(20, 40); // 移动起点到(20,40)
  3. _path.lineTo(80, 100); // 画条斜线
  4. _path.arcTo(new Rect.fromLTWH(60, 60, 100, 100), 0.0 * (pi / 180.0), 90.0 * (pi / 180.0), false);
  5. canvas.drawPath(_path, _paint);复制代码

拖着笔:

抬下笔:

封闭当前路径 - close()

  1. _paint.style = PaintingStyle.stroke; // 画线模式
  2. _path.moveTo(20, 40); // 移动起点到(20,20)
  3. _path.lineTo(80, 100); // 画条斜线
  4. _path.arcTo(new Rect.fromLTWH(60, 60, 100, 100), 0.0 * (pi / 180.0), 90.0 * (pi / 180.0), false);
  5. _path.close(); // 封闭当前路径
  6. canvas.drawPath(_path, _paint);复制代码

到这里Canvas图形的绘制就讲的差不多了,图形简单时,使用 drawCircle() drawRect() 等方法来直接绘制;图形复杂时,使用 drawPath() 来绘制自定义图形。 除此之外, Canvas 还可以绘制图片和文字。

画图片 - drawImage(Image image, Offset p, Paint paint) / drawImageRect(Image image, Rect src, Rect dst, Paint paint)

drawImage() 从指定点开始将图片宽高按像素绘制,由于无法控制图片的大小,并不常用;

第一个参数是ui包下的Image,并不是 Image Widget

Image 可以通过以下代码获取

  1. ui.Image image;
  2. /**
  3. * 初始化图片
  4. *
  5. Future<VoidCallback> initImage() async {
  6. image = await _loadImage("./assets/images/img.jpg");
  7. return null;
  8. }
  9. /**
  10. * 通过assets路径,获取资源图片
  11. */
  12. Future<Image> _loadImage(String assets) async {
  13. final ByteData data = await rootBundle.load(assets);
  14. if (data == null) throw 'Unable to read data';
  15. Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
  16. FrameInfo frame = await codec.getNextFrame();
  17. return frame.image;
  18. }复制代码

然后在initState() 方法中初始化(  shouldRepaint() 方法一定要记得返回 true  ,否则无法重绘

  1. void initState() {
  2. super.initState();
  3. painter.initImage().then((val) {
  4. setState(() {
  5. });
  6. });
  7. }复制代码

最后在 paint() 方法中填入以下代码:

  1. canvas.drawImage(image, new Offset(0, 0), _paint);复制代码

drawImageRect() 这个方法经常使用;主要了解第二个参数与第三个参数:

  • Rect src - 原图的区域,一般传图片的宽高
  • Rect dst - 显示的区域, 指图片显示的区域,如果原图区域宽高比与显示区域不一致,原图会被拉伸压缩
  1. canvas.drawImageRect(image, Offset(0.0, 0.0) & Size(image.width.toDouble(), image.height.toDouble()), Offset(0.0, 0.0) & Size(200, 200), _paint);复制代码

正常比例:

拉伸:

画文字 - drawParagraph(Paragraph paragraph, Offset offset)

代码注释的很清楚,这里循环画了5段文字

  1. for (int i = 0; i<5 ;i++){
  2. // 新建一个段落建造器,然后将文字基本信息填入;
  3. ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle(
  4. textAlign: TextAlign.left,
  5. fontWeight: FontWeight.w300,
  6. fontStyle: FontStyle.normal,
  7. fontSize: 15.0+i,
  8. ));
  9. pb.pushStyle(ui.TextStyle(color: Colors.black87));
  10. pb.addText('Flutter一统移动端');
  11. // 设置文本的宽度约束
  12. ParagraphConstraints pc = ParagraphConstraints(width: 300);
  13. // 这里需要先layout,将宽度约束填入,否则无法绘制
  14. Paragraph paragraph = pb.build()..layout(pc);
  15. // 文字左上角起始点
  16. Offset offset = Offset(50, 50+i*40.0);
  17. canvas.drawParagraph(paragraph, offset);
  18. }复制代码

Canvas及paint的使用基础部分大概就是这样了,下期文章见

Flutter自定义绘制(1)- 绘制基础的更多相关文章

  1. 自定义View入门-绘制基础(1)

    ### 前言 说道自定义View,我们一定会想到,自定义View的绘制流程 - 测量阶段(measure) - 布局阶段(layout) - 绘制阶段(draw) 我们看到的一些炫酷的view效果,都 ...

  2. 在WPF中自定义你的绘制(五)

    原文:在WPF中自定义你的绘制(五) 在WPF中自定义你的绘制(五)                                                                   ...

  3. 在WPF中自定义你的绘制(三)

    原文:在WPF中自定义你的绘制(三) 在WPF中自定义你的绘制(三)                                                                  ...

  4. 在WPF中自定义你的绘制(四)

    原文:在WPF中自定义你的绘制(四)                                   在WPF中自定义你的绘制(四)                                 ...

  5. 在WPF中自定义你的绘制(一)

    原文:在WPF中自定义你的绘制(一)   在WPF中自定义你的绘制(一)                                                                 ...

  6. 在WPF中自定义你的绘制(二)

    原文:在WPF中自定义你的绘制(二)   在WPF中自定义你的绘制(二)                                                                 ...

  7. ArcGIS Mobile 自定义图层在绘制面时节点未绘制完全的问题

    ArcGIS Mobile 自定义图层在绘制面时节点未绘制完全,如下图: 面的绘制代码如下: public void Draw(Display dis, Pen p1, Pen p2,Pen p3 , ...

  8. flutter自定义View(CustomPainter) 之 canvas的方法总结

    画布canvas 画布是一个矩形区域,我们可以控制其每一像素来绘制我们想要的内容 canvas 拥有多种绘制点.线.路径.矩形.圆形.以及添加图像的方法,结合这些方法我们可以绘制出千变万化的画面. 虽 ...

  9. IOS开发 图形绘制,绘制线条,矩形,和垂直和居中绘制文字

    概述 吐槽下IOS下 的图形绘图,代码冗长,不得不自己重新封装方法.整理形成本文. 绘制线 // 绘制直线 + (void)toDrawLineFromX:(CGFloat)x1 Y:(CGFloat ...

  10. 12-UIKit(View绘制、绘制曲线、绘制文字、贴图)

    目录: 1. View绘制 2. 绘制曲线 3. 绘制文字 4. 贴图 回到顶部 1. View绘制 1.1 做出自己的视图对象 TRCell : UITableViewCell : UIView U ...

随机推荐

  1. Java 处理json字符串value中多余的双引号

    转: Java 处理json字符串value中多余的双引号 一.错误场景 json字符串的value值中有多余的双引号 1.直接上错误的json字符串 1 String errorJsonStr =  ...

  2. 阶段5 3.微服务项目【学成在线】_day17 用户认证 Zuul_13-用户退出-前端

    调试前端的退出 logout方法 找到路由 退出对应的组件页面 这就是退出的组件 退出的方法 把这两个js的引用,从上面复制到下面引用.因为可能存在js的冲突问题. 资料里面给了一个前端 整个覆盖当前 ...

  3. 【423】COMP9024 Revision

    目录: array '\0' 与 EOF 二维字符数组(字符串数组) 1. array: 参考:C 数组 参考:C 字符串 参考:C笔记之NULL和字符串结束符'\0'和EOF 总结:[个人理解,可能 ...

  4. windows的mysql无法启动 服务没有报告任何错误

    相信很多人都遇到过安装Mysql的时候出现各种各样的问题,今天小编就教大家解决window下mysql服务没有报告任何错误的情况下无法启动 的问题.本文所用的mysql版本是5.7以上版本,解决方法: ...

  5. iOS使用UIImageView展现网络图片(转载)

    在iOS开发过程中,经常会遇到使用UIImageView展现来自网络的图片的情况,最简单的做法如下: [cpp] view plaincopy   - (void)viewDidLoad { [sup ...

  6. 【VS开发】网络SOCKET编程INADDR_ANY选项

    INADDR_ANY选项 网络编程中常用到bind函数,需要绑定IP地址,这时可以设置INADDR_ANY INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或&q ...

  7. 【VS开发】Wix 安装教程

    original link :  http://www.cnblogs.com/stoneniqiu/p/3355086.html 因为项目需要,最近在研究Wix打包部署,园子里也有一些关于wix ...

  8. 帝国cms 批量替换语句

    UPDATE www_92game_net_cnys_ecms_caipu SET titlepic=REPLACE(titlepic,'http://file.92game.net', '') ; ...

  9. 教你成为全栈工程师(Full Stack Developer) 四十五-一文读懂hadoop、hbase、hive、spark分布式系统架构

    转载自http://www.shareditor.com/blogshow?blogId=96 机器学习.数据挖掘等各种大数据处理都离不开各种开源分布式系统,hadoop用于分布式存储和map-red ...

  10. Hbuilder连接苹果手机

    最简单的连接方法 1     手机电脑连接---usb 2     pc下载iTunes 3     信任电脑,手机信任设备(设置--->通用--->设备管理-->信任) 4     ...