如何触摸油漆画布? [英] How to touch paint a canvas?

查看:90
本文介绍了如何触摸油漆画布?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

扑扑新手。我目前正在尝试使用Flutter构建一个简单的触摸绘图应用程序,但无法弄清楚如何触发画布重新绘制。

Flutter newbie here. I'm currently trying to build a simple touch-drawing app with Flutter, but cannot work out how to trigger a canvas re-draw.

我的意思是:
我有一个CustomPaint小部件,其中包含GestureDetector子级。每当发生触摸事件时,CustomPaint的画家都会收到一条消息,并存储触摸坐标以在重新绘制时绘制路径。问题是,永远不会调用paint方法。

What I have is this: I have a CustomPaint widget which contains a GestureDetector child. The CustomPaint's painter is getting a message whenever a touch event occurs, and stores the touch coordinates to draw a path on re-paint. Problem is, the paint method is never called.

这是我到目前为止的代码:

This is the code I have so far:

import 'package:flutter/material.dart';

class WriteScreen extends StatefulWidget {
  @override
  _WriteScreenState createState() => new _WriteScreenState();
}


class KanjiPainter extends CustomPainter {
  Color strokeColor;
  var strokes = new List<List<Offset>>();

  KanjiPainter( this.strokeColor );

  void startStroke(Offset position) {
    print("startStroke");
    strokes.add([position]);
  }

  void appendStroke(Offset position) {
    print("appendStroke");
    var stroke = strokes.last;
    stroke.add(position);
  }

  void endStroke() {
  }

  @override
  void paint(Canvas canvas, Size size) {
    print("paint!");
    var rect = Offset.zero & size;
    Paint fillPaint = new Paint();
    fillPaint.color = Colors.yellow[100];
    fillPaint.style = PaintingStyle.fill;
    canvas.drawRect(
      rect, 
      fillPaint
    );

    Paint strokePaint = new Paint();
    strokePaint.color = Colors.black;
    strokePaint.style = PaintingStyle.stroke;

    for (var stroke in strokes) {
      Path strokePath = new Path();
      // Iterator strokeIt = stroke.iterator..moveNext();
      // Offset start = strokeIt.current;
      // strokePath.moveTo(start.dx, start.dy);
      // while (strokeIt.moveNext()) {
      //   Offset off = strokeIt.current;
      //   strokePath.addP
      // }
      strokePath.addPolygon(stroke, false);
      canvas.drawPath(strokePath, strokePaint);
    }
  }

  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}


class _WriteScreenState extends State<WriteScreen> {
  GestureDetector touch;
  CustomPaint canvas;
  KanjiPainter kanjiPainter;

  void panStart(DragStartDetails details) {
    print(details.globalPosition);
    kanjiPainter.startStroke(details.globalPosition);
  }

  void panUpdate(DragUpdateDetails details) {
    print(details.globalPosition);
    kanjiPainter.appendStroke(details.globalPosition);
  }

  void panEnd(DragEndDetails details) {
    kanjiPainter.endStroke();
  }

  @override
  Widget build(BuildContext context) {
    touch = new GestureDetector(
      onPanStart: panStart,
      onPanUpdate: panUpdate,
      onPanEnd: panEnd,
    );

    kanjiPainter = new KanjiPainter( const Color.fromRGBO(255, 255, 255, 1.0) );

    canvas = new CustomPaint(
      painter: kanjiPainter,
      child: touch,
      // child: new Text("Custom Painter"),
      // size: const Size.square(100.0),
    );

    Container container = new Container(
      padding: new EdgeInsets.all(20.0),
      child: new ConstrainedBox(
        constraints: const BoxConstraints.expand(),
        child: new Card(
          elevation: 10.0,
          child: canvas,
        )
      )
    );

    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Draw!")
      ),
      backgroundColor: const Color.fromRGBO(200, 200, 200, 1.0),
      body: container,
    );
  }
}


推荐答案

CustomPainter文档,您必须在需要重新绘制时通知绘制小工具 p>

According to CustomPainter docs you must notify paint widget whenever repainting is needed


触发重新绘制的最有效方法是扩展此类并向CustomPainter的构造函数提供一个重新绘制参数,该对象将在此通知它的侦听器在需要重绘或扩展Listenable时(例如,通过ChangeNotifier进行扩展)并实现CustomPainter,以便对象本身直接提供通知。在这两种情况下,无论何时勾选动画,CustomPaint小部件或RenderCustomPaint渲染对象都将侦听Listenable并重新绘制,从而避免了管道的构建和布局阶段。

The most efficient way to trigger a repaint is to either extend this class and supply a repaint argument to the constructor of the CustomPainter, where that object notifies its listeners when it is time to repaint, or to extend Listenable (e.g. via ChangeNotifier) and implement CustomPainter, so that the object itself provides the notifications directly. In either case, the CustomPaint widget or RenderCustomPaint render object will listen to the Listenable and repaint whenever the animation ticks, avoiding both the build and layout phases of the pipeline.

例如 KanjiPainter 应该扩展 ChangeNotifier 并实现 CustomPainter 。当您更改笔触时,调用 notifyListeners

E.g. KanjiPainter should extend ChangeNotifier and implement CustomPainter. And when you change strokes, invoke notifyListeners

,同时调用 build 函数始终创建新的 KanjiPainter ,这将删除所有旧数据。您可以在 initState 中初始化画家一次。

And also build function always creates new KanjiPainter, this will remove all old data. You can init painter in initState once.

工作示例:

class WriteScreen extends StatefulWidget {
 @override
  _WriteScreenState createState() => new _WriteScreenState();
}

class KanjiPainter extends ChangeNotifier implements CustomPainter {
  Color strokeColor;
  var strokes = new List<List<Offset>>();

  KanjiPainter(this.strokeColor);

  bool hitTest(Offset position) => null;

  void startStroke(Offset position) {
    print("startStroke");
    strokes.add([position]);
    notifyListeners();
  }

  void appendStroke(Offset position) {
    print("appendStroke");
    var stroke = strokes.last;
    stroke.add(position);
    notifyListeners();
  }

  void endStroke() {
    notifyListeners();
  }

  @override
  void paint(Canvas canvas, Size size) {
    print("paint!");
    var rect = Offset.zero & size;
    Paint fillPaint = new Paint();
    fillPaint.color = Colors.yellow[100];
    fillPaint.style = PaintingStyle.fill;
    canvas.drawRect(rect, fillPaint);

    Paint strokePaint = new Paint();
    strokePaint.color = Colors.black;
    strokePaint.style = PaintingStyle.stroke;

    for (var stroke in strokes) {
      Path strokePath = new Path();
      // Iterator strokeIt = stroke.iterator..moveNext();
      // Offset start = strokeIt.current;
      // strokePath.moveTo(start.dx, start.dy);
      // while (strokeIt.moveNext()) {
      //   Offset off = strokeIt.current;
      //   strokePath.addP
      // }
      strokePath.addPolygon(stroke, false);
      canvas.drawPath(strokePath, strokePaint);
    }
  }

  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class _WriteScreenState extends State<WriteScreen> {
  GestureDetector touch;
  CustomPaint canvas;
  KanjiPainter kanjiPainter;

  void panStart(DragStartDetails details) {
    print(details.globalPosition);
    kanjiPainter.startStroke(details.globalPosition);
  }

  void panUpdate(DragUpdateDetails details) {
    print(details.globalPosition);
    kanjiPainter.appendStroke(details.globalPosition);
  }

  void panEnd(DragEndDetails details) {
    kanjiPainter.endStroke();
  }

  @override
  void initState() {
    super.initState();
    kanjiPainter = new KanjiPainter(const Color.fromRGBO(255, 255, 255, 1.0));
  }

  @override
  Widget build(BuildContext context) {
    touch = new GestureDetector(
      onPanStart: panStart,
      onPanUpdate: panUpdate,
      onPanEnd: panEnd,
    );

    canvas = new CustomPaint(
      painter: kanjiPainter,
      child: touch,
      // child: new Text("Custom Painter"),
      // size: const Size.square(100.0),
    );

    Container container = new Container(
        padding: new EdgeInsets.all(20.0),
        child: new ConstrainedBox(
            constraints: const BoxConstraints.expand(),
            child: new Card(
              elevation: 10.0,
              child: canvas,
            )));

    return new Scaffold(
      appBar: new AppBar(title: new Text("Draw!")),
      backgroundColor: const Color.fromRGBO(200, 200, 200, 1.0),
      body: container,
    );
  }
}

这篇关于如何触摸油漆画布?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆