Flutter第6期 自定义widget

说起自定义控件,做过iOS和android开发的肯定都不陌生,flutter中也提供了相关的api。flutter中原声提供的各类widget已经可以满足了大部分的业务需求,通过不同的组合,我们也能实现一些较为复杂的需求。今天我们来讲一个需要自己绘制的自定义widget。在Android中,我们是通过自定义view的draw->paint->canvas等一些列的api绘制,flutter中同样也提供了方法。

class TestPainter extends CustomPainter{

  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return oldDelegate != this;
  }

}

我们先来自定义个Painter,可以看到这个Painter继承了CustomPainter。我们需要实现两个方法,paint相当于Android 中的ondraw,只不过参数中多了一个size参数。

参数size

我们看到size提供了多个构造方法,最主要的是宽和高两个属性。这个相当于我们在android中自定义view的onmeasure方法。可以看到flutter中的size更加的灵活。

参数canvas

写过android自定义view的同学,看到这个api是不是更加的眼熟了。没错大部分的api都是相同的。

---------------------------------------------------------------------------------------------------------------------------------------------------------------

下面我们来写个demo。先来看看效果图:

我们现在来拆分一下

  • 六边形
  • 内部网格
  • 外部圆形
  • 6个角的文字
  • 动画

开始撸代码~

  1. 创建widget

  2. 创建自定义CustomPainter

  3. 添加绘制算法

1.创建widget

2.创建自定义CustomPainter并初始化

3.绘制方法

绘制文字的时候,要注意文字的宽度和高度

---------------------------------------------------------------------------------------------------------------------------------------------------------------

完整代码

import 'dart:core';
import 'dart:ui' as ui;
import 'dart:ui';
import 'dart:math';
import 'package:flutter/material.dart';

class PageTest2 extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new PageTest2State();
  }
}

class PageTest2State extends State with SingleTickerProviderStateMixin {
  List<double> _ranks = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
  AnimationController _controller;
  Animation _curve;

  @override
  void initState() {
    _controller = AnimationController(duration: const Duration(milliseconds: 1000), vsync: this);
    _curve = new CurvedAnimation(parent: _controller, curve: Curves.fastLinearToSlowEaseIn);
    _curve.addListener(() {
      setState(() {});
    });
    _controller.forward();
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _ranks = generateRanks();
          _controller.reset();
          _controller.forward();
        },
        child: Text("按"),
      ),
      body: Container(
        constraints: BoxConstraints(
          minWidth: double.infinity,
          minHeight: double.infinity,
        ),
        child: Center(
          child: Container(
            constraints: BoxConstraints(
              minWidth: 200,
              minHeight: 200,
            ),
            child: CustomPaint(
              painter: TestPainter(_curve.value, _ranks),
            ),
          ),
        ),
      ),
    );
  }

  List<double> generateRanks() {
    List<double> ranks = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
    for (int i = 0; i < ranks.length; i++) {
      ranks[i] = Random().nextDouble() * 100;
    }
    return ranks;
  }
}

class TestPainter extends CustomPainter {
  ​​double _process;// 动画的进度
  Paint _polygonPaint;// 绘制六边形的画笔
  Paint _frameStrokePaint;// 绘制六边形网格的画笔
  Paint _rankPaint;// 绘制中心等级的画笔
  double _centerX;// 自定义TestPainter中心x
  double _centerY;// 自定义TestPainter中心y
  double _sideLength;// 正六边形边长
  double _centerCircleRadius;// 中心园半径
  List<double> _ranks;// 等级数据
  List<String> _labelString = const ["体力", "精神", "物理", "魔法", "防御", "金钱"];

  TestPainter(this._process, this._ranks) {
    _polygonPaint = new Paint()
      ..isAntiAlias = true
      ..style = PaintingStyle.fill //填充
      ..color = Color(Colors.blueGrey.value);

    _frameStrokePaint = new Paint()
      ..isAntiAlias = true
      ..style = PaintingStyle.stroke //描边
      ..color = Color(Colors.green.value);

    _rankPaint = new Paint()
      ..isAntiAlias = true
      ..style = PaintingStyle.fill //填充
      ..color = Color(Colors.red.withAlpha(130).value);

    _sideLength = 120;
    _centerCircleRadius = 120;
  }

  @override
  void paint(Canvas canvas, Size size) {
    _centerX = size.width / 2.0;
    _centerY = size.height / 2.0;

    _drawPolygon(canvas, _sideLength);
    _drawPolygonFrame(canvas, _sideLength, 4);
    _drawCircle(canvas);
    _drawRank(canvas, _sideLength, _ranks);
    _drawText(canvas, _sideLength);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
  // 绘制文字
  void _drawText(Canvas canvas, double slidLength) {
    for (int i = 0; i < _labelString.length; i++) {
      String label = _labelString[i] + "+" + (_ranks[i] * _process).toStringAsFixed(1);
      TextSpan span = new TextSpan(
        style: TextStyle(color: Colors.black),
        text: label,
      );
      TextPainter tp = new TextPainter(
          text: span,
          textAlign: TextAlign.center,
          textDirection: TextDirection.ltr);
      tp.layout();
      double endX = (_centerX + slidLength * sin(pi / 3 * i));
      double endY = (_centerY - slidLength * cos(pi / 3 * i));
      if (i == 0) {
        endX = endX - tp.width / 2;
        endY = endY - tp.height;
      } else if (i == 1) {
        endX = endX;
        endY = endY - tp.height / 2;
      } else if (i == 2) {
        endX = endX;
        endY = endY - tp.height / 2;
      } else if (i == 3) {
        endX = endX - tp.width / 2;
      } else if (i == 4) {
        endX = endX - tp.width;
        endY = endY - tp.height / 2;
      } else {
        endX = endX - tp.width;
        endY = endY - tp.height / 2;
      }
      tp.paint(canvas, new Offset(endX, endY));
    }
  }
  // 绘制圆形
  void _drawCircle(Canvas canvas){
    canvas.drawCircle(Offset(_centerX, _centerY), _centerCircleRadius, _frameStrokePaint);
  }
  // 绘制中心等级
  void _drawRank(Canvas canvas, double slidLength, List<double> ranks) {
    Path path = new Path();
    double percentLength = slidLength / 100 * _process;
    path.moveTo(_centerX, _centerY - ranks[0] * percentLength); //起始坐标
    for (int i = 0; i < 6; i++) {
      double rankRadius = ranks[i] * percentLength;
      double endX = (_centerX + rankRadius * sin(pi / 3 * i));
      double endY = (_centerY - rankRadius * cos(pi / 3 * i));
      path.lineTo(endX, endY);
    }
    path.close();
    canvas.drawPath(path, _rankPaint);
  }
  // 绘制中心六边形网格
  void _drawPolygonFrame(Canvas canvas, double slidLength, int row) {
    for (int k = 1; k <= row; k++) {
      double tempSlideLength = slidLength / row * k;
      Path path = new Path();
      path.moveTo(_centerX, _centerY - tempSlideLength);
      for (int i = 0; i < 6; i++) {
        double endX = (_centerX + tempSlideLength * sin(pi / 3 * i));
        double endY = (_centerY - tempSlideLength * cos(pi / 3 * i));
        path.lineTo(endX, endY);
        canvas.drawLine(
            Offset(_centerX, _centerY), Offset(endX, endY), _frameStrokePaint);
      }
      path.close();
      canvas.drawPath(path, _frameStrokePaint);
    }
  }
  // 绘制六边形底
  void _drawPolygon(Canvas canvas, double slidLength) {
    Path path = new Path();
    path.moveTo(_centerX, _centerY - slidLength);
    for (int i = 0; i < 6; i++) {
      double endX = (_centerX + slidLength * sin(pi / 3 * i));
      double endY = (_centerY - slidLength * cos(pi / 3 * i));
      path.lineTo(endX, endY);
    }
    path.close();
    canvas.drawPath(path, _polygonPaint);
  }
}

猜你喜欢

转载自blog.csdn.net/z2008q/article/details/103958172