如何触摸油漆画布?
问题内容:
扑扑新手。我目前正在尝试使用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
docs,
您必须在需要重绘时通知油漆小部件
触发重画的最有效方法是扩展此类并向CustomPainter的构造函数提供重画参数,该对象在需要重画时通知其侦听器,或扩展Listenable(例如,通过ChangeNotifier)并实现CustomPainter,以便对象本身直接提供通知。在这两种情况下,无论何时勾选动画,CustomPaint小部件或RenderCustomPaint渲染对象都将侦听Listenable并重新绘制,从而避免了管道的构建和布局阶段。
例如,KanjiPainter
应该扩展ChangeNotifier
并实施CustomPainter
。当您更改笔触时,notifyListeners
并且build
函数总是创建new KanjiPainter
,这将删除所有旧数据。您可以initState
一次启动画家。
工作示例:
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,
);
}
}