highlight: a11y-dark

0.前言

有很多人问我如何绘制虚线,一直没有这方面需求,没有太在意。现在想一下,通过路径测量实现虚线绘制应该是非常简单的。就抽了点空,顺手写个好用的虚线路径绘制工具,不然平时画个辅助线啥的确实挺费劲。

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_Android 绘制 虚线 颜色太淡


该绘制工具 dash_painter 已经上传到 pub

  • pub 地址: https://pub.dev/packages/dash_painter
  • github 地址: https://github.com/toly1994328/dash_painter

| 圆角矩形 | 圆形 | | ------------------------------------------------------------ | ------------------------------------------------------------ | |

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_ide_02

|

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_ide_03

|


1. 实现的绘制

如下画板,通过路径绘制出一条直线,这应该是绘制最基础的东西了,不多介绍。下面来看一下如何实现将它变成一条虚线

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_ci_04

```dart class TolyPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { canvas.translate(size.width / 2, size.height / 2); Paint paint = Paint()..style = PaintingStyle.stroke ..color=Colors.orangeAccent..strokeWidth = 2;

Path path = Path();
path.moveTo(-100, 0);
path.lineTo(100, 0);

canvas.drawPath(path, paint);

}

@override bool shouldRepaint(covariant TolyPainter oldDelegate) => false; } ```


2.绘制虚线 - level1

为了方便管理和拓展,可以将虚线对象分离出一个类 DashPainter。既然要画虚线,自然要明确相关的虚线参数,这里先来个简单的。  虚线的 单线长间距 分别使用 stepspan 表示,如下是一个 step:20, span: 10 的虚线。

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_flutter_05

```dart class DashPainter { final double step; final double span;

const DashPainter({this.step = 2, this.span = 2,});

double get partLength => step + span;

void paint(Canvas canvas, Path path, Paint paint) { final PathMetrics pms = path.computeMetrics(); pms.forEach((PathMetric pm) { final int count = pm.length ~/ partLength; for (int i = 0; i < count; i++) { canvas.drawPath( pm.extractPath(partLength * i, partLength * i + step), paint); } final double tail = pm.length % partLength; canvas.drawPath(pm.extractPath(pm.length-tail, pm.length), paint); }); } } ```

实现的逻辑也非常简单: 对路径进行 computeMetrics,然后根据份数遍历绘制截取的路径即可。使用时也非常简单,只要一句即可化实为虚

dart const DashPainter(span: 10, step: 20).paint(canvas, path, paint);


通过控制 stepspan 参数,可以控制虚线的显示效果。

| step:6, span: 6 | step:6, span: 4 | | :----------------------------------------------------------- | ------------------------------------------------------------ | |

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_ide_06

|

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_dash_07

|


其实到这里,就可以让 任意路径 虚线化,如下的圆角矩形和圆形:

dart final Path path = Path(); path.addRRect(RRect.fromRectAndRadius( Rect.fromCircle(center: Offset.zero, radius: 100), Radius.circular(20), )); const DashPainter(span: 4, step: 9).paint(canvas, path, paint);

| 圆角矩形 | 圆形 | | ------------------------------------------------------------ | ------------------------------------------------------------ | |

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_dash_08

|

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_flutter_09

|


2.绘制虚线 - level2

除了虚线,有时还会有点划线 的需求,如下

  • 单点划线

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_Android 绘制 虚线 颜色太淡_10

  • 双点划线

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_Android 绘制 虚线 颜色太淡_11

  • 三点划线

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_ci_12


代码实现如下,增加了 pointCountpointWidth两个属性,分别表示点划线数点划线长。其实整体思路是不变的, stepspan 还是那个含义,只不过单体的长度 pointLineLength 需要根据 pointCountpointWidth 进行加长,如下图所示:

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_ci_13

```dart class DashPainter { const DashPainter({ this.step = 2, this.span = 2, this.pointCount = 0, this.pointWidth, });

final double step; final double span; final int pointCount; final double pointWidth;

void paint(Canvas canvas, Path path, Paint paint) { final PathMetrics pms = path.computeMetrics(); final double pointLineLength = pointWidth ?? paint.strokeWidth; final double partLength = step + span * (pointCount + 1) + pointCount * pointLineLength;

pms.forEach((PathMetric pm) {
  final int count = pm.length ~/ partLength;
  for (int i = 0; i < count; i++) {
    canvas.drawPath(
        pm.extractPath(partLength * i, partLength * i + step), paint,);
    for (int j = 1; j <= pointCount; j++) {
      final start =
          partLength * i + step + span * j + pointLineLength * (j - 1);
      canvas.drawPath(
        pm.extractPath(start, start + pointLineLength),
        paint,
      );
    }
  }
  final double tail = pm.length % partLength;
  canvas.drawPath(pm.extractPath(pm.length - tail, pm.length), paint);
});

} } ```


这样就可以完成一下很棒的东西,比如点画线圆

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_ci_14

dart final Path path = Path(); path.moveTo(-200, 0); path.lineTo(200, 0); path.moveTo(0, -200); path.lineTo(0, 200); path.addOval(Rect.fromCircle(center: Offset.zero, radius: 100)); const DashPainter( span: 4, // 空格长 step: 10, // 实线长 pointCount: 2, // 点划线个数 pointWidth: 2 // 点划线长 ).paint(canvas, path, paint);

3.装饰绘制

可能很多人不会自定义画板自己绘制,或只想简单地使用。其实除了 CustomPainter 还有其他地方有 canvas。比如 Decoration 。我们可以自定义 DashDecoration 的装饰,方便使用。这里只是一个简单的使用,可以基于此封装一下配置属性。

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_ide_15

```dart class DashDecoration extends Decoration {
@override BoxPainter createBoxPainter([onChanged]) => const DashBoxPainter(); }
class DashBoxPainter extends BoxPainter { final Color color; const DashBoxPainter({this.color});
@override void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { canvas.save(); final Paint paint = Paint() ..style = PaintingStyle.stroke ..color = Colors.orangeAccent ..strokeWidth = 1; final Path path = Path();
canvas.translate(
  offset.dx + configuration.size.width / 2,
  offset.dy + configuration.size.height / 2,
);

final Rect zone = Rect.fromCenter(
  center: Offset.zero,
  width: configuration.size.width,
  height: configuration.size.height,
);

path.addRRect(RRect.fromRectAndRadius(
  zone,
  Radius.circular(20),
));

const DashPainter(span: 4, step: 9).paint(canvas, path, paint);
canvas.restore();

} } ```


这属性配置些在库中已经封装,可以直接使用,如下实现一个渐变的单点画线圆角虚线框

Android 绘制 虚线 颜色太淡 可以画虚线的手机软件_dash_16

dart Container( width: 100, height: 100, decoration: DashDecoration( pointWidth: 2, step: 5, pointCount: 1, radius: Radius.circular(15), gradient: SweepGradient(colors: [ Colors.blue, Colors.red, Colors.yellow, Colors.green ])), child: Icon( Icons.add, color: Colors.orangeAccent, size: 40, ), ),

本文就到这里,这个工具还有很多优化拓展的空间,后面有时间或灵感时会持续维护,希望能对你有所帮助。