Flutter 浅析之 自定义view (自定义图形) 二

17 篇文章 0 订阅
12 篇文章 2 订阅

技术无止境,只怕不学习啊,Flutter 我们开始吧

今天我们就按照这个步骤来实现下自定义View

CustomPainter的使用
新建类继承于CustomPainter并且实现CustomPainter里面的paint()和shouldRepaint方法。

class TestPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
  }
  
  @override
  bool 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;   //画笔的宽度

  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    canvas.drawLine(Offset(20.0, 20.0), Offset(100.0, 100.0), _paint);
  }

  @override
  bool 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.

  @override
  Widget 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> {

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    var 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(线,相邻连接)
代码:

 @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    canvas.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

 @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    canvas.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

  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    canvas.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即可。
用法:

 @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    canvas.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种方式无论你使用那种都是一样的,都可以确定这个矩形的位置和大小,淡然这个椭圆也是在这个矩形之中内切的

@override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    Rect 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π
例子:

 @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    double 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看下效果。

 @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    double 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用来表示圆角的大小。

 @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    Rect 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大小

 @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    Rect 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来完成绘制。

  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    Rect 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
在这里插入图片描述

可以看到两个圆角矩形哦,淡然我们可以尝试调整角度的度数大小。

  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    Rect 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);
  }

效果:
在这里插入图片描述

@override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    Rect 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);
  }

效果:
在这里插入图片描述
可以调整两个圆弧的位置来获得交叉的圆弧效果。

本篇文章就到这里,下一篇继续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值