JavaFX8 演示视图(重复窗格和内容)

JavaFX8 presentation view (duplicate pane and content)

我正在尝试为我的绘图应用程序创建演示视图,其中只有绘图板(窗格)可见。因此,用户可以在投影仪上显示演示视图,同时在 PC 上拥有实际的绘图窗格和工具。

我目前的做法是从每一帧的窗格中创建一个快照,并在第二阶段的 ImageView 中显示它。

public void startStream(){

    new AnimationTimer() {
        @Override
        public void handle(long now) {
            WritableImage image = drawingPane.snapshot(new SnapshotParameters(), null);
            stream.setImage(image);
        }
    }.start();

    final Screen screen = Screen.getPrimary();
    final Stage stage = new Stage();
    StackPane root = new StackPane();
    root.getChildren().add(stream);
    Scene scene = new Scene(root, drawingPane.getWidth(), drawingPane.getHeight());
    stage.setScene(scene);
    stage.setTitle("streaming stage");

    Rectangle2D bounds = screen.getBounds();
    System.out.println(bounds);
    stage.setX(bounds.getMinX() + (bounds.getWidth() - 300) / 2);
    stage.setY(bounds.getMinY() + (bounds.getHeight() - 200) / 2);
    stage.show();
}

这里的问题是它占用了大量 RAM,比如 800MB,而且 CPU 使用率增加了 30%。我可以理解,在每一帧上创建图像效率不高,这就是为什么我想问一下,是否有办法使它更有效地工作。

此外,如果有更好的 approach/solution 此功能,我将不胜感激。

为了减少 CPU 的使用,不要在 AnimationTimer 中制作快照——这实际上是以帧速率 (60 FPS) 频率制作快照,即使 drawingPane.相反,在 drawingPane 的布局之后制作快照。将侦听器添加到 drawingPaneneedsLayoutProperty 应该可以解决问题:

drawingPane.needsLayoutProperty().addListener((obs, wasDirty, isDirty) -> {
    if(!isDirty) {
        WritableImage image = drawingPane.snapshot(new SnapshotParameters(), null);
        stream.setImage(image);
    }
});

当您不编辑 drawingPane 时,这应该会降低 CPU 的使用率。

它也可能会降低内存使用率,因为垃圾收集器可能会更快启动并收集旧快照。如果不是,请考虑 re-using WritableImage 实例,如果它已经具有正确的大小:

WritableImage image = null;

drawingPane.needsLayoutProperty().addListener((obs, wasDirty, isDirty) -> {
    if(!isDirty) {
        if(this.image != null &&
                (this.image.getWidth() != drawingPane.getWidth() ||
                 this.image.getHeight() != drawingPane.getHeight())) {
            this.image = null;
        }
        this.image = drawingPane.snapshot(new SnapshotParameters(), this.image);
        stream.setImage(this.image);
    }
});

注意,此答案假设 drawingPane 的 children 是 managed children(这是默认设置),因此它们的布局被标记为 dirty 导致 drawingPane 的布局被标记为脏的,因此您可以实际观察 needsLayout 属性.