我有一个简单的 CustomPaint/CustomPainter 可以绘制一段圆(下面的代码)。我读过我不能使用 GestureDetector 因为它不是一个合适的小部件,那么获取输入的最佳方式是什么?

我将有一堆片段放在一起,所以我需要像素精确的触摸位置。

我想到了两种可能:

  • 将painter放入一个SizedBox,获取触摸坐标,手动计算是否在路径内。但这会使大量代码加倍。
  • 使用 Material 类和自定义 BorderShape。这会很方便,但对我来说似乎很笨拙。

  • 我的自定义画家:
    class _SegmentPainter extends CustomPainter {
      static const offset = -pi/2;
      double start;
      double end;
      double innerRadius;
      double outerRadius;
      Color color;
      _SegmentPainter(this.start, this.end, {this.innerRadius = 0.0, this.outerRadius, this.color});
    
      @override bool shouldRepaint(CustomPainter oldDelegate) => this == oldDelegate;
      @override bool shouldRebuildSemantics(CustomPainter oldDelegate) => this == oldDelegate;
    
      @override
      void paint(Canvas canvas, Size size) {
        Path path = new Path();
        path.arcTo(Rect.fromCircle(center: new Offset(0.0, 0.0), radius: outerRadius), offset + start, end-start, true);
        path.relativeLineTo(-cos(offset + end)*(outerRadius-innerRadius), -sin(offset + end)*(outerRadius-innerRadius));
        path.arcTo(Rect.fromCircle(center: new Offset(0.0, 0.0), radius: innerRadius), offset + end, start-end, false);
        path.close();
    
        canvas.drawPath(path, new Paint()..color = color..style = PaintingStyle.fill);
      }
    }
    

    最佳答案

    我同意您必须将 CustomPainter 放在具有大小的小部件中。它可能是一个 SizedBox,所以我在这里使用了它。幸运的是,您不需要进行手动 HitTest ,因为 CustomPainter 可以通过一些重构为您处理。首先要注意的是 path 不需要在每个 paint() 上重建 - 它可以在构造函数中构建。这允许 CustomPainter 的 hitTest 简单地询问点击是在路径内部还是外部。

    class _SegmentPainter extends CustomPainter {
      static const offset = -pi / 2;
    
      double start;
      double end;
      double innerRadius;
      double outerRadius;
      Color color;
    
      Path path;
    
      _SegmentPainter(
          this.start, this.end, this.innerRadius, this.outerRadius, this.color) {
        path = new Path()
          ..arcTo(
              Rect.fromCircle(center: new Offset(0.0, 0.0), radius: outerRadius),
              offset + start,
              end - start,
              true)
          ..relativeLineTo(-cos(offset + end) * (outerRadius - innerRadius),
              -sin(offset + end) * (outerRadius - innerRadius))
          ..arcTo(
              Rect.fromCircle(center: new Offset(0.0, 0.0), radius: innerRadius),
              offset + end,
              start - end,
              false)
          ..close();
      }
    
      @override
      bool shouldRepaint(_SegmentPainter oldDelegate) {
        return oldDelegate.start != start ||
            oldDelegate.end != end ||
            oldDelegate.innerRadius != innerRadius ||
            oldDelegate.outerRadius != outerRadius ||
            oldDelegate.color != color;
      }
    
      @override
      bool shouldRebuildSemantics(_SegmentPainter oldDelegate) => true;
    
      @override
      void paint(Canvas canvas, Size size) {
        canvas.drawPath(
            path,
            new Paint()
              ..color = color
              ..style = PaintingStyle.fill);
      }
    
      @override
      bool hitTest(Offset position) {
        return path.contains(position);
      }
    }
    
    class SegmentWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new GestureDetector(
          onTap: () => print('tap'),
          child: new SizedBox(
            width: 250.0,
            height: 250.0,
            child: new CustomPaint(
              painter: new _SegmentPainter(0.0, 2.8, 150.0, 200.0, Colors.orange),
            ),
          ),
        );
      }
    }
    

    我使用 Dart ..(级联)语法来清理路径。 (我认为您的 should... 测试被否定了。)我添加了一个 StatelessWidget 作为 SizedBoxGestureDetector 的家。

    10-08 03:27