bugfix> dart > 投稿

円のセグメントを描画する簡単なCustomPaint/CustomPainterがあります(以下のコード)。 GestureDetectorは適切なウィジェットではないため使用できないことを読みましたが、入力を取得する最良の方法は何ですか?

たくさんのセグメントを一緒にするので、ピクセル単位の正確なタッチ位置が必要です。

私が考えた2つの可能性:

  • ペインターを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);
  }
}

回答 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... だと思う  テストは否定されました。) SizedBox のホームとしてStatelessWidgetを追加しました  および GestureDetector

あなたの答え