颤动:即使shouldRepaint()返回true

时间:2020-07-11 13:12:23

标签: c++ flutter dart ffi

我正在通过从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.

1 个答案:

答案 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");
  }

}

GOTCHAS

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

}
相关问题