如何触摸画布?

2023-11-29

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

我所拥有的是这样的: 我有一个 CustomPaint 小部件,其中包含一个 GestureDetector 子部件。每当发生触摸事件时,CustomPaint 的绘制器都会收到一条消息,并存储触摸坐标以在重新绘制时绘制路径。问题是,paint 方法从未被调用。

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

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 的构造函数提供重绘参数,其中该对象在需要重绘时通知其侦听器,或者扩展 Listenable(例如通过 ChangeNotifier)并实现CustomPainter,以便对象本身直接提供通知。无论哪种情况,CustomPaint 小部件或 RenderCustomPaint 渲染对象都将侦听 Listenable 并在动画发生时重新绘制,从而避免管道的构建和布局阶段。

E.g. KanjiPainter应该延长ChangeNotifier并实施CustomPainter。当你改变笔画时,调用notifyListeners

并且build函数总是创造新的KanjiPainter,这将删除所有旧数据。您可以在中初始化画家initState once.

工作示例:

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

class KanjiPainter extends ChangeNotifier implements CustomPainter {
  Color strokeColor;
  var strokes = <List<Offset>>[];

  KanjiPainter(this.strokeColor);

  bool hitTest(Offset position) => true;

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

  @override
  // TODO: implement semanticsBuilder
  SemanticsBuilderCallback? get semanticsBuilder => null;

  @override
  bool shouldRebuildSemantics(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRebuildSemantics
    return true;
  }
}

class _WriteScreenState extends State<WriteScreen> {
  late GestureDetector touch;
  late CustomPaint canvas;
  late 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,
    );
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何触摸画布? 的相关文章

随机推荐

  • 可以多次打开DB4o文件进行查询、插入、更新吗?

    这就是我想到的使用 DB4o 的方式 当我需要查询时 我会打开文件 读取并关闭 using IObjectContainer db Db4oFactory OpenFile Db4oFactory NewConfiguration YapF
  • 为什么java.lang.Object无法克隆?

    当我尝试克隆泛型时Object我收到编译时错误 为什么 Object obj new Object obj clone Here compile time error The method clone from the type Objec
  • 如何删除列中超过 2 个连续的 NA?

    我是 R 新手 在我的数据框中我有 col1 Timestamp col2 Values 我必须删除 col2 中超过 2 个连续 NA 的行 我的数据框看起来像下面的一个 Timestamp values 2011 01 02 2 201
  • Express.js 应用程序错误:无法读取未定义的属性“transfer-encoding”

    我正在研究一个博客应用程序 点击链接即可查看GitHub回购 与Express EJS和 MongoDB 我正在尝试介绍一个添加帖子图片特征 作为 Express 的新手 我对遇到的问题感到困惑 添加帖子表单
  • 从本地文件发出 XML 跨域请求

    我不确定这是否可能 基本上我想在客户端 PC 上加载本地 html 文件并让它向远程服务器发出请求 服务器提供的数据是XML 当我说我正在加载文件时 我的意思是 Chrome 中的 URL 显示为 file E 这是我最接近能够加载 XML
  • 如何有条件地更新 Pandas 中的 DataFrame 列

    有了这个DataFrame 我如何有条件地设置rating为 0 时line race等于零 line track line race rating foreign 25 MTH 10 84 False 26 MTH 6 88 False
  • 如何处理图像中的浮点坐标值

    我正在图像上绘制一些几何图形 问题是坐标以浮点数形式出现 所以我无法绘制它们 作为解决办法 我正在使用 Floor 将其截断为最接近的整数 这在某些情况下效果很好 只是它稍微改变了我的形象 x 9 7 x floor x 9 the plo
  • 您是否会将 NHibernate 用于具有遗留数据库的项目,而该数据库部分超出了您的控制范围?

    对我来说 目前的答案是 不 我会使用 iBatis 因为当数据库模型和对象模型不同步时 NHibernate 很痛苦 如果我不能完全控制数据库 我最终会做很多工作 我为什么要问 好吧 首先 我从未使用过 NHibernate 我只是从表面上
  • 使用 SWIG 从 C++ 回调到 C#

    我有一个在 C Net Core 和 C Windows 应用程序中运行的应用程序 我使用 SWIG 实现了 C 和 C 之间的互操作性 但我无法实现从 C 到 C 的回调功能 我还尝试将函数指针从 C 传递到 C 但也失败了 我的意图是通
  • python argparse,如何通过名称引用参数

    这是一个关于python中argparse的问题 它可能很简单 import argparse parser argparse ArgumentParser parser add argument lib args parser parse
  • 单击后更改 wpf C# 中按钮的颜色,并在 2 分钟后保留原始颜色

    我正在使用这个代码 Hello Background System Windows Media Brushes Blue var dispatcherTimer new DispatcherTimer dispatcherTimer Int
  • UnicodeWarning:Tkinter 中的特殊字符

    我用 Tkinter Python 2 7 编写了一个程序 这是一个挪威语的拼字游戏助手 其中包含一些特殊字符 这意味着我的单词列表 ordliste 包含带有特殊字符的单词 当我运行函数 finnord c 时 它返回 cd 我正在使用一
  • 使用 Xcode 4.3.x 构建适用于 iOS 3.0 的通用应用程序 - NSKeyedUnarchiver 异常

    尝试使用 Xcode 4 3 2 构建和调试适用于 iOS 3 0 的通用应用程序 我从 NSKeyedUnarchiver 收到 NSException 看来这可能与无法读取主 xib 文件有关 我见过这个 iOS 和取消归档 xib 文
  • __init__() 恰好需要 3 个参数(给定 1 个)

    我今天刚开始学习Python 所以如果这是一个简单的问题 我很抱歉 我花了半个小时尝试纠正以下代码 class Area def init self width height self width width self height hei
  • MySQL 查询优化 - JOIN?

    适合所有 MySQL 专家的一款 我有以下查询 SELECT o p name p amount p quantity FROM orders o products p WHERE o id p order id AND o total 0
  • C中int指针的初始化

    关于在 C 中初始化 int 指针的一个非常简单的问题 我是刚刚通知那 int varname 0 无效 我还没有找到指出这一点的明确参考 但有信心 基于评论者代表 它可能无效 即使它可以编译 构建和接受来自 calloc malloc 语
  • Maven + Surefire 在测试失败时返回代码为 0

    我有一个项目 测试分为单元阶段和集成阶段 我让它运行 buildbot 问题是即使在测试失败时 maven 返回代码也是 0 所以 buildbot 构建是成功的 这是 mvn 集成测试的结果 Results Tests in error
  • 将自定义 UIButton 添加到 UIKeyboard 的 UIWebView 附件视图

    我需要添加一个相机按钮UIWebView的键盘附件视图工具栏 已经具有 后退 下一步 和 完成 按钮的工具栏 是否有捷径可寻 我仍在寻找更好的方法来做到这一点 但目前的一个解决方案是销毁该栏并重新创建它 如下所示 void viewDidL
  • 应用程序脚本 - function () { [本机代码] }

    我正在制作一个谷歌应用程序脚本 我正在尝试制作一个可以读取第一个 callendar 事件的程序 问题是 当我尝试从中获取任何内容时 它只会写 function 本机代码 我看过一些为 javascript 编写的问题 但我不明白如何将 j
  • 如何触摸画布?

    这里是颤振新手 我目前正在尝试使用 Flutter 构建一个简单的触摸绘图应用程序 但无法弄清楚如何触发画布重新绘制 我所拥有的是这样的 我有一个 CustomPaint 小部件 其中包含一个 GestureDetector 子部件 每当发