如何解决只有前景发生变化时,Flutter才会保留背景画布绘制以跳过重绘
我正在一个项目中,该项目的CustomPaint
绘制形状作为背景,当您点击屏幕时,它将在该特定位置绘制一个圆圈。我正在使用GestureDetector
获取拍子数据并将其作为自变量发送到_MyCustomPainter
,类似于下面的代码:
class MyApp extends StatefulWidget{
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Offset _circlePosition = Offset(-1,-1);
void setCirclePosition(Offset newPosition) {
setState(() {
this._circlePosition = newPosition;
});
}
void clearCircle() {
setState(() {
this._circlePosition = Offset(-1,-1);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,title: Text('My app'),),body: Container(
child: GestureDetector(
onTapDown: (detail) {
setCirclePosition(detail.localPosition);
},onHorizontalDragStart: (detail) {
setCirclePosition(detail.localPosition);
},onHorizontalDragUpdate: (detail) {
setCirclePosition(detail.localPosition);
},onVerticalDragStart: (detail) {
setCirclePosition(detail.localPosition);
},onVerticalDragUpdate: (detail) {
setCirclePosition(detail.localPosition);
},onTapUp: (detail) {
clearCircle();
},onVerticalDragEnd: (detail) {
clearCircle();
},onHorizontalDragEnd: (detail) {
clearCircle();
},child: LimitedBox(
maxHeight: 400,maxWidth: 300,child: CustomPaint(
size: Size.infinite,painter: new _MyCustomPainter(
circlePosition: circlePosition,);
}
}
class _MyCustomPainter extends CustomPainter {
_MyCustomPainter({
this.circlePosition,});
final Offset circlePosition;
void _drawBackground(Canvas canvas) {
// draw the background
}
@override
void paint(Canvas canvas,Size size) {
_drawBackground(canvas);
if (circlePosition.dx != -1 && circlePosition.dy != -1) {
// draws a circle on the position
var circlePaint = Paint()..color = Colors.green;
canvas.drawCircle(circlePosition,5,circlePaint);
}
}
@override
bool shouldRepaint(_MyCustomPainter old) {
return circlePosition.dx != old.circlePosition.dx ||
circlePosition.dy != old.circlePosition.dy;
}
}
这就是问题,每当用户长按一下手指在屏幕上移动手指时,都会反复绘制不变的背景,并且绘制此特定背景涉及很多代码。有shouldRepaint
方法,但它发出信号来重新绘制整个小部件。我怎样才能使背景仅绘制一次,然后在重绘时我只使用之前创建的背景?是使用PictureRecorder
生成图像,而不是在将来使用该图像重画最佳方法?我的CustomPainter
是否应该扩展ChangeNotifier
以获得更好的性能?当前的方法可行,但是我想知道如何改进它。
解决方法
因此,我用来实现此目的的方法是使用两个CustomPainters,一个用于背景(我们称其为BackgroundPainter),一个用于前驱(例如ForegroundPainter),当BackgroundPaiter.shouldRepaint方法返回true时,将其绘制在画布并将其保存到图像中,然后在ForegroundPainter上使用该图像。我必须对此烛台图表小部件here进行此操作。这段代码的简短版本类似于下面的示例:
class MyWidget extends StatefulWidget {
MyWidget({
this.foregroundParam,this.backgroundParam,});
final String foregroundParam;
final String backgroundParam;
@override
MyWidget createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Picture backgroundPicture;
_BackgroundPainter backgroundPainter;
Size parentSize;
Size oldParentSize;
@override
Widget Build(BuildContext context) {
// We need the context size,which is only available after
// the build method finishes.
WidgetsBinding.instance.addPostFrameCallback((_) {
// this code only runs after build method is run
if (parentSize != context.size) {
setState(() {
parentSize = context.size;
});
}
});
var newBackgroundPainter = _BackgroundPainter(
backgroundParam: widget.backgroundParam,);
// update backgroundPicture and backgroundPainter if parantSize was updated
// (like after a screen rotation) or if backgroundPainter was updated
// based on the backgroundParam
if (parentSize != null &&
(oldParentSize != null && oldParentSize != parentSize ||
backgroundPainter == null ||
newBackgroundPainter.shouldRepaint(backgroundPainter) ||
backgroundPicture == null)
) {
oldParentSize = parentSize;
backgroundPainter = newBackgroundPainter;
var recorder = PictureRecorder();
var canvas = Canvas(recorder,Rect.fromLTWH(0,parentSize.width,parentSize.height));
backgroundPainter.paint(canvas,parentSize);
setState(() {
backgroundPicture = recorder.endRecording();
});
}
// if there is no backgroundPicture,this must be the first run where
// parentSize was not set yet,so return a loading screen instead,but
// this is very quick and most likely will not even be noticed. Could return
// an empty Container as well
if (backgroundPicture == null) {
return Container(
width: double.infinity,height: double.infinity,child: Center(
child: CircularProgressIndicator(),),);
}
// if everything is set,return an instance of _ForegroundPainter passing
// the backgroundPicture as parameter
return CustomPaint(
size: Size.infinite,painter: _ForegroundPainter(
backgroundPicture: backgroundPicture,foregroundParam: widget.foregroundParam,);
}
}
class _BackgroundPainter extends CustomPainter {
_BackgroundPainter({
this.backgroundParam,});
final String backgroundParam;
@override
void paint(Canvas canvas,Size size) {
// paint background here
}
@override
bool shouldRepaint(_BackgroundPainter oldPainter) {
return backgroundParam != oldPainter.backgroundParam;
}
}
class _ForegroundPainter extends CustomPainter {
_ForegroundPainter({
this.backgroundPicture,this.foregroundParam,});
final Picture backgroundPicture;
final String foregroundParam;
@override
void paint(Canvas canvas,Size size) {
canvas.drawPicture(backgroundPicture);
// paint foreground here
}
@override
bool shouldRepaint(_ForegroundPainter oldPainter) {
return foregroundParam != oldPainter.foregroundParam ||
backgroundPicture != oldPainter.backgroundPicture;
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。