Flutter自定义绘制Widget初探,安卓未来路在何方

Radius bottomLeft: Radius.zero
}
)

// 顶点X轴Y轴的圆角设置相同
const Radius.circular(double radius)
// 顶点X轴Y轴的圆角设置可以不相同
const Radius.elliptical(this.x, this.y)

绘制圆角矩形的方法

void drawRRect(RRect rrect, Paint paint)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-5.png

// 利用矩形的左边的X坐标、矩形顶部的Y坐标、矩形右边的X坐标、矩形底部的Y坐标
// 以及一个Radius来确定圆角矩形
RRect.fromLTRBR(double left, double top, double right, double bottom, Radius radius)

// 需要先定义一个Rect,使用方式和fromLTRBR类似
RRect.fromRectAndRadius(Rect rect, Radius radius)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-6.png

// 利用矩形的左边的X坐标、矩形顶部的Y坐标、矩形右边的X坐标、矩形底部的Y坐标
// 以及设置四个顶点的X轴相同圆角,设置四个顶点的Y轴相同圆角来确定圆角矩形
RRect.fromLTRBXY(double left, double top, double right, double bottom, double radiusX, double radiusY)

// 需要先定义一个Rect,使用方式和fromLTRBXY类似
RRect.fromRectXY(Rect rect, double radiusX, double radiusY)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-7.png

圆角矩形绘制有一个需要注意的地方,如果设置矩形的半径和圆角的半径相等,则绘制出来的是一个圆

// 设置矩形的半径和圆角的半径不相等,效果如下面左图
Rect rect = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 50.0);
RRect rRect = RRect.fromRectAndRadius(rect, Radius.circular(10.0));
// 设置矩形的半径和圆角的半径相等,效果如下面右图
Rect rect = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 50.0);
RRect rRect = RRect.fromRectAndRadius(rect, Radius.circular(50.0));

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-8.png

2.5 绘制双圆角矩形

Flutter中提供了绘制双圆角矩形的方法

void drawDRRect(RRect outer, RRect inner, Paint paint)

这里需要注意的是,内圆角矩形的半径不能大于外圆角矩形的半径,否则无法绘制。

// 第一种:外圆角矩形的半径大于内圆角矩形半径,如下面左图
Rect rectOut = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 80.0);
Rect rectInner = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 40.0);
// 第二种:外圆角矩形的半径等于内圆角矩形半径,如下面中图
Rect rectOut = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 80.0);
Rect rectInner = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 80.0);
// 第三种:外圆角矩形的半径小于内圆角矩形半径,无法绘制,如下面右图
Rect rectOut = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 80.0);
Rect rectInner = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 81.0);

接下来绘制上面三种情况下的双圆角矩形

RRect rRectOut = RRect.fromRectAndRadius(rectOut, Radius.circular(10.0));
RRect rRectInner = RRect.fromRectAndRadius(rectInner, Radius.circular(30.0));

canvas.drawDRRect(rRectOut, rRectInner, _paint);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-9.png

2.6 绘制圆

// 根据圆心坐标和圆的半径确定圆的位置和大小void drawCircle(Offset c, double radius, Paint paint)

画一个圆

// 绘制圆,Paint默认填充PaintingStyle.fill,如下面左图canvas.drawCircle(Offset(100.0, 100.0), 50.0, _paint);// 绘制圆,Paint设置为不填充,如下面右图_paint.style = PaintingStyle.stroke;canvas.drawCircle(Offset(100.0, 100.0), 50.0, _paint);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-10.png

2.7 绘制椭圆

// Rect矩形区域的确定参见前文
void drawOval(Rect rect, Paint paint)
// 椭圆的宽度大于高度,如下面左图
Rect rect= Rect.fromPoints(Offset(50.0, 50.0), Offset(130.0, 100.0));

// 椭圆的宽度小于高度,如下面中图
Rect rect= Rect.fromPoints(Offset(40.0, 80.0), Offset(80.0, 170.0));

// 椭圆的宽度等于高度,绘制出的是圆,如下面右图
Rect rect= Rect.fromPoints(Offset(80.0, 70.0), Offset(180.0, 170.0));

绘制一个椭圆

canvas.drawOval(rect, _paint);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-11.png

2.8 绘制圆弧

// rect:矩形区域// startAngle:开始的弧度// sweepAngle:扫过的弧度,正数顺时针,负数逆时针// useCenter:是否使用中心点绘制void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)

弧度

一周的弧度数为2πr/r=2π,360°角=2π弧度。

因此,1弧度约为57.3°,即57°17’44.806’’,1°为π/180弧度,近似值为0.01745弧度。
周角为2π弧度,平角(即180°角)为π弧度,直角为π/2弧度。

下表是一些特殊的角度和弧度之间的换算

角度弧度
0
30°π/6
45°π/4
60°π/3
90°π/2
120°2π/3
180°π
270°3π/2
360°

使用π需要引入:

import ‘dart:math’;

下面我们尝试绘制一些圆弧

var rect = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 50.0);
// 开始度数为90度,顺时针扫度数90度,如下方左图
canvas.drawArc(rect, -pi / 2, pi / 2, false, _paint);

// 开始度数为0度,顺时针扫度数60度,如下方中图
canvas.drawArc(rect, 0, pi / 3, false, _paint);

// 开始度数为0度,逆时针扫度数180度,使用中心点绘制,如下方右图
canvas.drawArc(rect, 0, -pi, true, _paint);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-12.png

2.9 绘制路径

void drawPath(Path path, Paint paint)

Path方法介绍

// 将路径的起始点移动到指定的坐标点上
void moveTo(double x, double y)

// 将起始点和终点连成线段,如果没有显式的指明起始点坐标,默认为(0,0)
void lineTo(double x, double y)
// 起始点(30,30),终点(100,100),如下面左图
Path path = new Path();
path.moveTo(30, 30);
path.lineTo(100, 100);
canvas.drawPath(path, _paint);

// 起始点默认(0,0),终点(100,100),如下面右图
Path path = new Path();
path.lineTo(100, 100);
canvas.drawPath(path, _paint);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-13.png

// 相对于当前位置坐标在X轴上偏移dx,在Y轴上偏移dy
void relativeMoveTo(double dx, double dy)
// 起始点(0,0),和(20,20)之间连线,后移动到(30,30),和(70,70)之间连线
Path path = new Path();
path.lineTo(20, 20);
path.moveTo(30, 30);
path.lineTo(70, 70);
canvas.drawPath(path, _paint);

// 起始点(0,0),和(20,20)之间连线,
// 后以当前(20,20)坐标为起始点X轴偏移30,Y轴偏移30,
// 即偏移后的新坐标(50,50)和(70,70)之间连线
Path path = new Path();
path.lineTo(20, 20);
path.relativeMoveTo(30, 30);
path.lineTo(70, 70);
canvas.drawPath(path, _paint);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-14.png

// 确定相对于当前位置坐标,在X轴上偏移dx,在Y轴上偏移dy后的新坐标,
// 确定好之后当前位置坐标和新坐标之间连线
void relativeLineTo(double dx, double dy)
// (0,0)和(10,10)连线,(10,10)和(20,20)连线,如下面左图
Path path = new Path();
path.lineTo(10, 10);
path.lineTo(20, 20);
canvas.drawPath(path, _paint);

// (0,0)和(10,10)连线,(10,10)和当前位置偏移后的新坐标(30,30)连线
Path path = new Path();
path.lineTo(10, 10);
path.relativeLineTo(20, 20);
canvas.drawPath(path, _paint);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-15.png

// 绘制二阶贝塞尔曲线
// 绘制需要一个起始点、一个终点和一个控制点
// 此方法的前两个参数是控制点的坐标,后两个参数是终点的坐标
void quadraticBezierTo(double x1, double y1, double x2, double y2)

// 绘制二阶贝塞尔曲线
// 绘制需要一个起始点、一个终点和一个控制点
// 此方法的前两个参数是控制点的坐标,后两个参数是终点的坐标
// 和quadraticBezierTo不同在于终点坐标的位置是当前起点位置坐标X轴偏移x2,Y轴偏移y2后的坐标
// 举例(略)
void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2)
// 绘制曲线,如下面左图
Path path = new Path();
path.moveTo(150, 100);
path.quadraticBezierTo(250, 40, 250, 200);
canvas.drawPath(path, _paint);
// 绘制控制点
_paint.strokeWidth = 8;
_paint.color = Colors.blue;
canvas.drawPoints(PointMode.points, [Offset(250, 40)], _paint);
// 绘制曲线,如下面中图
Path path = new Path();
path.moveTo(150, 100);
path.quadraticBezierTo(200, 200, 250, 200);
canvas.drawPath(path, _paint);
// 绘制控制点
_paint.strokeWidth = 8;
_paint.color = Colors.blue;
canvas.drawPoints(PointMode.points, [Offset(200, 200)], _paint);
// 绘制曲线,如下面右图
Path path = new Path();
path.moveTo(150, 100);
path.quadraticBezierTo(60, 130, 150, 200);
canvas.drawPath(path, _paint);
// 绘制控制点
_paint.strokeWidth = 8;
_paint.color = Colors.blue;
canvas.drawPoints(PointMode.points, [Offset(60, 130)], _paint);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-16.png

// 绘制三阶贝塞尔曲线
// 绘制需要一个起始点、一个终点和两个控制点
// 此方法的前四个参数分别是两个控制点的XY坐标,后两个参数是终点的坐标
void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3)

// 绘制三阶贝塞尔曲线
// 绘制需要一个起始点、一个终点和两个控制点
// 此方法的前四个参数分别是两个控制点的XY坐标,后两个参数是终点的坐标
// 和cubicTo不同在于终点坐标的位置是当前起点位置坐标X轴偏移x2,Y轴偏移y2后的坐标
// 举例(略)
void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3)

下面使用cubicTo绘制心形效果

var width = 300;
var height = 300;

Path leftPath = new Path();
// 画笔移动到左边曲线起点
leftPath.moveTo(width / 2, height / 4 + 20);
leftPath.cubicTo(
width / 8, height / 10,
width / 13, (height * 2) / 5,
width / 2, height - height / 4);
// 绘制心形左边曲线
canvas.drawPath(leftPath, _paint);

Path rightPath = new Path();
// 画笔移动到右边曲线起点
rightPath.moveTo(width / 2, height / 4 + 20);
rightPath.cubicTo(
width - width / 8, height / 10,
(width * 12) / 13, (height * 2) / 5,
width / 2, height - height / 4);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

点击这里免费领取吧!

mg-4ghrdXI7-1710660813673)]
[外链图片转存中…(img-BOx7sQWY-1710660813673)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-wpgT7J7y-1710660813674)]

[外链图片转存中…(img-NJOLQqDV-1710660813674)]

[外链图片转存中…(img-lPGFYKD2-1710660813674)]

点击这里免费领取吧!

  • 15
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter中的自定义Controller是指可以控制和管理特定组件状态的类。通过自定义Controller,我们可以实现对组件的状态进行监听、更新和控制。 在Flutter中,常用的自定义Controller是StatefulWidget的Controller,也称为StateController。StateController通常包含一个State对象,用于管理组件的状态,并提供一些方法来更新状态和通知组件重新构建。 下面是一个简单的示例,展示了如何创建一个自定义的Controller来管理一个计数器的状态: ```dart class CounterController { int _count = 0; int get count => _count; void increment() { _count++; } } ``` 在上面的示例中,CounterController包含一个私有变量_count来保存计数器的值,并提供了一个公共方法increment来增加计数器的值。通过get方法count,我们可以获取当前计数器的值。 在使用自定义Controller时,通常需要将其与StatefulWidget配合使用。下面是一个使用CounterController的示例: ```dart class CounterWidget extends StatefulWidget { @override _CounterWidgetState createState() => _CounterWidgetState(); } class _CounterWidgetState extends State<CounterWidget> { final CounterController _controller = CounterController(); @override Widget build(BuildContext context) { return Column( children: [ Text('Count: ${_controller.count}'), RaisedButton( child: Text('Increment'), onPressed: () { setState(() { _controller.increment(); }); }, ), ], ); } } ``` 在上面的示例中,CounterWidget使用CounterController来管理计数器的状态。在build方法中,我们可以通过_controller.count获取当前计数器的值,并通过_controller.increment方法来增加计数器的值。当点击按钮时,我们调用setState方法来通知Flutter框架重新构建组件。 通过自定义Controller,我们可以更好地管理和控制组件的状态,使代码更加模块化和可维护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值