flutter 用 CustomPaint 畫一個自定義的 CircleProgressBar (一)

tabbin發表於2019-07-17

什麼是CustomPaint?

在Android中,有時候我們需要使用 Canvas 和 Paint 來進行繪製一些複雜的圖形,在 Flutter 中,當然也有可以讓你自由繪製的方案,他就是 CustomPaint。

CustomPaint 也是一個 Widget,你可以把它嵌到檢視樹的任意一個節點位置。

先看看它的常用屬性:

屬性 型別 說明
painter CustomPainter 背景畫筆,繪製內容會顯示在child子節點後面
foregroundPainter CustomPainter 前景畫筆,繪製內容會顯示在child子節點前面
size Size 設定繪製區域的大小。如果有child,則忽略該引數,且繪製區域為child的尺寸
isComplex bool 是否複雜的繪製,如果是,Flutter會應用一些快取策略來減少重複渲染的開銷。預設false
willChange bool 和isComplex配合使用,當啟用快取時,該屬性代表在下一幀中繪製是否會改變。預設false
child Widget 沒錯,CustomPaint是可以包含一個子節點的

CustomPaint是一個繼承自SingleChildRenderObjectWidget的控制元件,不能用setState的方式來重新整理它。

painter就是我們的主繪製工具,它是一個CustomPainterforegroundPainter是用來繪製前景的工具;

size為畫布大小,這個size會傳遞給Painter

isComplexwillChange 是告訴Flutter你的CustomPaint是否複雜到需要使用cache相關的功能;

child屬性我們一般不填,即使你是想要在你的CustomPaint上新增一些其他的佈局,也不建議放在child屬中性,因為你會發現你並不會得到你想要的結果。

CustomPainter 是一個抽象類,你需要繼承它實現自己的邏輯。

class MyPainter extends CustomPainter {

  @override
  paint(Canvas canvas, Size size)  {
       // do
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

繼承 CustomPainter 最重要的是實現 paint(Canvas canvas, Size size)shouldRepaint(CustomPainter oldDelegate) 這兩個函式。

  • paint() 中是繪製邏輯,可以在這獲得畫布 Canvas 和 畫布的大小 Size
  • shouldRepaint() 返回 true 才會進行重繪,否則就只會繪製一次。你可以通過一些條件判斷來決定是否每次繪製,這樣能夠節約系統資源。

例如:

@override
  void paint(Canvas canvas, Size size) {
    final outerThickess = max(trackWidth, max(progressWidth, thumbSize));
    Size constrainedSize = new Size(
      size.width - outerThickess,
      size.height - outerThickess
    );
    // paint track.
    final center = new Offset(size.width / 2, size.height /2);
    final radius = min(constrainedSize.width, constrainedSize.height) / 2;
    canvas.drawCircle(center, radius, trackPaint);

    // paint progress.
    final progressAngle = 2 * pi * progressPercent;
    canvas.drawArc(new Rect.fromCircle(
      center: center,
      radius: radius
    ), -pi / 2, progressAngle, false, progressPaint);

    // paint thumb.
    final thumbAngle = 2 * pi * thumbPosition - (pi / 2);
    final thumbX = cos(thumbAngle) * radius;
    final thumbY = sin(thumbAngle) * radius;
    final thumbCenter = new Offset(thumbX, thumbY) + center;
    final thumbRadius = thumbSize / 2;
    canvas.drawCircle(thumbCenter, thumbRadius, thumbPaint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }

這裡簡單的列一下,一些常用的canvas繪製API:

// 繪製弧線
drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
// 繪製圖片
drawImage(Image image, Offset p, Paint paint) 
// 繪製圓
drawCircle(Offset c, double radius, Paint paint) 
// 繪製線條
drawLine(Offset p1, Offset p2, Paint paint) 
// 繪製橢圓
drawOval(Rect rect, Paint paint)
// 繪製文字
drawParagraph(Paragraph paragraph, Offset offset)
// 繪製路徑
drawPath(Path path, Paint paint) 
// 繪製點
drawPoints(PointMode pointMode, List<Offset> points, Paint paint)
// 繪製Rect
drawRect(Rect rect, Paint paint) 
// 繪製陰影
drawShadow(Path path, Color color, double elevation, bool transparentOccluder)

1、繪製直線

void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..isAntiAlias = true
      ..color = Colors.pink
      ..strokeWidth = 10
      ..style = PaintingStyle.fill;
    Paint paint1 = Paint()
      ..isAntiAlias = true
      ..color = Colors.blue
      ..strokeWidth = 10
      ..style = PaintingStyle.fill;
    Paint paint2 = Paint()
      ..isAntiAlias = true
      ..color = Colors.green
      ..strokeWidth = 10
      ..style = PaintingStyle.fill;
    /// Offset(),橫縱座標偏移
    canvas.drawLine(Offset(85, 100), Offset(285, 100), paint);
    canvas.drawLine(Offset(85, 100), Offset(200, 300), paint1);
    canvas.drawLine(Offset(285, 100), Offset(200, 300), paint2);
  }

2、繪製弧線

void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..isAntiAlias = true
      ..color = Colors.green
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 10
      ..style = PaintingStyle.stroke;
    Paint paint1 = Paint()
      ..isAntiAlias = true
      ..color = Colors.blue
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 10
      ..style = PaintingStyle.stroke;
    /// Offset(),橫縱座標偏移
    /// void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
    /// Rect來確認圓弧的位置, 開始的弧度、結束的弧度、是否使用中心點繪製(圓弧是否向中心閉合)、以及paint.
    final center = new Offset(170, 200);
    canvas.drawArc(new Rect.fromCircle(
        center: center,
        radius: size.width / 2
    ), -pi / 2, 2 * pi * 0.5, false, paint);
    canvas.drawArc(new Rect.fromCircle(
        center: Offset(170, 300),
        radius: size.width / 2
    ), pi / 2, 2 * pi * 0.5, false, paint1);
  }

flutter 用CustomPaint畫一個自定義的CircleProgressBar

3、繪製圓

 Paint paint = Paint()
      ..isAntiAlias = true
      ..color = Colors.green
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 10
      ..style = PaintingStyle.fill;
    Paint paint1 = Paint()
      ..isAntiAlias = true
      ..color = Colors.blue
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 10
      ..style = PaintingStyle.stroke;
      canvas.drawCircle(Offset(150.0, 200.0), 50.0, paint1);
      canvas.drawCircle(Offset(150.0, 350.0), 50.0, paint);

flutter 用CustomPaint畫一個自定義的CircleProgressBar

相關文章