我正在通过从C / C ++端获取颜色数据并使用CustomPainter
绘制屏幕来测试抖动FFI。
但是令我惊讶的是,即使我将绘画者设置为始终重新绘画,paint()
函数也只被调用了两次。
class _ColorViewPainter extends CustomPainter {
Color clrBackground;
_ColorViewPainter({
this.clrBackground = Colors.black
});
@override
bool shouldRepaint(_ColorViewPainter old) => true;
@override
void paint(Canvas canvas, Size size) {
print("paint: start");
final color = ffiGetColor().ref;
final r = color.r;
final g = color.g;
final b = color.b;
print("color: $r, $g, $b");
final paint = Paint()
..strokeJoin = StrokeJoin.round
..strokeWidth = 1.0
..color = Color.fromARGB(255, r, g, b)
..style = PaintingStyle.fill;
final width = size.width;
final height = size.height;
final content = Offset(0.0, 0.0) & Size(width, height);
canvas.drawRect(content, paint);
print("paint: end");
}
}
ffiGetColor()
函数仅从C / C ++端检索彩色RGB结构。我可以看到屏幕更新了两次,日志显示:
I/flutter (12096): paint: start
I/flutter (12096): color: 255, 0, 0
I/flutter (12096): paint: end
I/flutter (12096): paint: start
I/flutter (12096): color: 0, 255, 0
I/flutter (12096): paint: end
I/Surface (12096): opservice is null false
就是这样。即使我显然希望它用shouldRepaint
重新绘制。扑扑未能做到。
怎么了?
这是我的环境
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel dev, v1.18.0-dev.5.0, on Mac OS X 10.15.5 19F101, locale en-CA)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 11.5)
[✓] Android Studio (version 4.0)
[✗] Cannot determine if IntelliJ is installed
✗ Directory listing failed
[✓] VS Code (version 1.44.2)
[✓] Connected device (1 available)
! Doctor found issues in 1 category.
答案 0 :(得分:2)
由于@pskink的提示,尽管它仍然不完美,但我设法使CustomPainter连续重绘。
请参见dart/flutter: CustomPaint updates at a lower rate than ValueNotifier's value update
由于当前解决方案仍然存在更新率问题,因此我只简要介绍一下:
我基本上必须构造一个通知程序,并通过轮询在适当的位置为其分配ffi检索的值。
// in initState() of State class
_notifier = ValueNotifier<NativeColor>(ffiGetColor().ref);
...
// in a timer method
_notifier.value = ffiGetColor().ref;
然后使用CustomPaint
,绑定通知程序以在其构造函数中重新绘制
@override
Widget build(BuildContext context) {
return Container(
...
child: CustomPaint(
painter: _ColorViewPainter(
context: context,
notifier: _notifier,
...
)
)
);
}
class _ColorViewPainter extends CustomPainter {
ValueNotifier<NativeColor> notifier;
BuildContext context;
Color clrBackground;
_ColorViewPainter({this.context, this.notifier, this.clrBackground})
: super(repaint: notifier) {
}
@override
bool shouldRepaint(_ColorViewPainter old) {
print('should repaint');
return true;
}
@override
void paint(Canvas canvas, Size size) {
print("paint: start");
final r = notifier.value.r;
final g = notifier.value.g;
final b = notifier.value.b;
print("color: $r, $g, $b");
final paint = Paint()
..strokeJoin = StrokeJoin.round
..strokeWidth = 1.0
..color = Color.fromARGB(255, r, g, b)
..style = PaintingStyle.fill;
final width = size.width;
final height = size.height;
final content = Offset(0.0, 0.0) & Size(width, height);
canvas.drawRect(content, paint);
print("paint: end");
}
}
ValueNotifier
依靠以下条件能够向其侦听器发出通知
operator ==
ValueNotifier
的value字段,使用更改后的新数据对象显式重新分配。当我们将通知程序绑定到自定义类时,这至关重要。对于自定义类,我们必须
operator ==
。ValueNotifier<MyClass>.value
,而不是通过调用其自己的常规方法来修改数据对象的值。否则,CustomPaint
的{{1}}不会在所需的更改上被调用。
举个例子,这个自定义类不适合与paint()
绑定,因为没有重载的ValueNotifier
:
operator==
再举一个例子,假设我们有一个自定义类:
class MyClass {
int prop = 0;
void changeValue(newValue) {
prop = newValue;
}
}
这将起作用:
class MyClass {
int prop = 0;
@override
bool operator ==(covariant MyClass other) {
return other is MyClass && prop != other. prop;
}
void changeValue(newValue) {
prop = newValue;
}
}
但这是行不通的。
class _MyViewState extends State<MyView> {
ValueNotifier<MyClass> notifier;
Timer _timer;
....
@override
initState() {
super.initState();
notifier = ValueNotifier<MyClass>(MyClass());
_timer = Timer.periodic(Duration(milliseconds: 10), _updateData);
}
_updateData(Timer t) {
var myObj = MyClass(newValue);
notifier.value = myObj;
}
}