【问题标题】:Flutter - Getting touch input on CustomPaintersFlutter - 在 CustomPainters 上获取触摸输入
【发布时间】:2018-05-24 21:24:58
【问题描述】:

我有一个简单的 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);
  }
}

【问题讨论】:

标签: dart flutter


【解决方案1】:

在 CustomPaint 小部件中设置子项:Container()!

InkWell(
      child: CustomPaint(painter: HexagonPainter(center, radius, color), child: Container(),),
      onTap: (){
        print('------yumcoder---');
      },

示例:

class HexagonPaint extends StatelessWidget {
  final Offset center;
  final double radius;
  final Color color;

  HexagonPaint(this.center, this.radius, this.color);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      child: CustomPaint(painter: HexagonPainter(center, radius, color), child: Container(),),
      onTap: (){
        print('------yumcoder---');
      },
    );
  }
}

class HexagonPainter extends CustomPainter {
  static const int SIDES_OF_HEXAGON = 6;
  final double radius;
  final Offset center;
  final Color color;

  HexagonPainter(this.center, this.radius, this.color);

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()..color = this.color;
    Path path = createHexagonPath();
    canvas.drawPath(path, paint);
  }

  Path createHexagonPath() {
    final path = Path();
    var angle = (math.pi * 2) / SIDES_OF_HEXAGON;
    Offset firstPoint = Offset(radius * math.cos(0.0), radius * math.sin(0.0));
    path.moveTo(firstPoint.dx + center.dx, firstPoint.dy + center.dy);
    for (int i = 1; i <= SIDES_OF_HEXAGON; i++) {
      double x = radius * math.cos(angle * i) + center.dx;
      double y = radius * math.sin(angle * i) + center.dy;
      path.lineTo(x, y);
    }
    path.close();
    return path;
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

【讨论】:

    【解决方案2】:

    我开发了一个名为 touchable 用于此目的。

    只需使用 CanvasTouchDetector 包裹您的 CustomPaint 小部件。它需要一个构建器函数作为参数,它需要您的 CustomPaint 小部件,如下所示。

    CanvasTouchDetector(
        builder: (context) => 
            CustomPaint(
                painter: MyPainter(context)
            )
    )
    

    在您的 CustomPainter 类的 paint 方法中,创建并使用 TouchyCanvas 对象(使用从 CanvasTouchDetector 和画布获得的上下文)通过不同的手势回调绘制任何形状。

    var myCanvas = TouchyCanvas(context,canvas);
    myCanvas.drawRect( rect , Paint() , onTapDown: (tapDetail){
        //Do stuff here. Probably change your state and animate
    });
    

    【讨论】:

      【解决方案3】:

      我同意您必须将 CustomPainter 放在具有大小的小部件中。它可能是一个 SizedBox,所以我在这里使用了它。幸运的是,您不需要进行手动命中测试,因为 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 的家。

      【讨论】:

      • 啊,我在Canvas 文档中寻找类似contains 的东西!太好了,谢谢。
      • 附带说明,should... 测试并不总是返回 true 会导致不必要的重绘?
      • 应该真的需要比较 oldDelgate 看看是否有任何变化。我将通过适当的实施来更新答案。
      猜你喜欢
      • 1970-01-01
      • 2021-10-22
      • 1970-01-01
      • 1970-01-01
      • 2017-01-03
      • 2011-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多