一尘不染

Flutter-在CustomPainters上获得触摸输入

flutter

我有一个简单的CustomPaint /
CustomPainter,它绘制了一个圆的段(下面的代码)。我已经读到我不能使用GestureDetector,因为它不是正确的小部件,那么获取输入的最佳方法是什么?

我将有很多段,所以我需要像素精确的触摸位置。

我想到了两种可能性:

  • 将画家放在SizedBox中并获取触摸坐标,然后手动计算它是否在路径内。但这将使很多代码加倍。
  • 使用Material类和自定义BorderShape。这将很方便,但对我来说似乎很客气。

我的CustomPainter:

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);
  }
}

阅读 492

收藏
2020-08-13

共1个答案

一尘不染

我同意您必须将CustomPainter放入具有大小的窗口小部件中。它可能是一个SizedBox,所以我在这里使用了它。幸运的是,您不需要进行手动命中测试,因为CustomPainter可以通过一些重构即可为您处理。首先要注意的是,不需要在每个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,作为SizedBoxand
的主页GestureDetector

2020-08-13