JavaFX canvas - 仅在 canvas 可见时绘制

JavaFX canvas - draw only when the canvas is visible

我正在考虑将我们的内部 automotive/measuring 应用程序从 Swing 移植到 JavaFX,主要是因为更好的外观和多点触控支持,但我找不到仅在自定义组件可见时呈现它们的方法.

出于动机,想象一个有 10 个选项卡的屏幕,每个选项卡内都有一个图表,显示一些正在测量的实时数据。任何时候,只能看到一个情节。数据量大,计算机有足够的能力一次渲染一个图,但不能同时全部绘制。

摇摆版

现在在 Swing 版本中,此行为的实现很简单。每个绘图都是一个自定义的 JComponent,具有覆盖所有绘图的 paintComponent 方法。 paintComponent 在两种重要情况下被调用:

比这个更复杂(详见http://www.oracle.com/technetwork/java/painting-140037.html),但大致是这样的。重要的是只有可见的图也在处理和重绘..

移植到 JavaFX?

现在我正尝试将此行为移植到 JavaFX,自然选择是 Canvas。但是paintComponent回调机制没有了,直接画到Canvas.getGraphicsContext2D里面就行了。

如果我理解正确的话,在此上下文中调用绘图操作并不会真正执行任何立即绘图,它只是将操作推送到堆栈上,稍后在 Prism 线程运行时由 Prism 子系统处理(这是正确的吗? ?)。所以,可能(我只是猜测,我对 JavaFX 很陌生),如果 Prism 发现 Canvas 不可见(选择了另一个选项卡,它被另一个节点,位于屏幕的可见部分之外,...),这是正确的吗?那就太好了。

但即使是这种情况,对我也没有帮助,因为我什至不想 开始 渲染,直到我知道 Canvas可见。即使对实时数据进行预处理以进行绘图也可能需要相当多的 CPU 功率,甚至可能比渲染本身还要多 - 想象一下插值数千个数据点来绘制几条线。所以,在实时数据变化之后,我想在开始之前先测试每个地块的实际可见性(Canvas)它的重绘。当它 变得 可见时,我也需要得到通知。这在某种程度上可以用 JavaFX 实现吗?还是我和 Canvas 一起走了一条糟糕的路线,有更好的方法吗?

P.S。请不要将这种可见性与 Node.visibleProperty 混淆,后者做了一些不同的事情,基本上关闭了节点的渲染。

使用选项卡窗格,我认为唯一的方法是观察选项卡窗格的选定项(即选定的选项卡)。您可能想对您的绘图区域进行编码,使其独立于它是否显示在选项卡窗格中,因此我会在绘图中包含一个 BooleanProperty,指示它是否处于活动状态:

public class PlotPane extends Pane {
    private final BooleanProperty active = new SimpleBooleanProperty();
    public BooleanProperty activeProperty() {
        return active ;
    }
    public final boolean isActive() {
        return activeProperty().get();
    }
    public final void setActive(boolean active) {
        activeProperty().set(active);
    }

    public PlotPane(String name) {
        // just for demo...
        activeProperty().addListener((obs, wasActive, isActive) -> {
            if (isActive) {
                System.out.println(name + " is active");
            } else {
                System.out.println(name + " is inactive");
            }
        });
    }
}

然后当您 assemble 将它放入选项卡窗格时,您可以绑定 属性:

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class ActiveTabTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        PlotPane pane1 = new PlotPane("Pane1");
        Tab tab1 = new Tab("Pane1", pane1);

        PlotPane pane2 = new PlotPane("Pane2");
        Tab tab2 = new Tab("Pane2", pane2);

        TabPane tabPane = new TabPane(tab1, tab2);

        pane1.activeProperty().bind(tabPane.getSelectionModel().selectedItemProperty().isEqualTo(tab1));
        pane2.activeProperty().bind(tabPane.getSelectionModel().selectedItemProperty().isEqualTo(tab2));


        primaryStage.setScene(new Scene(tabPane, 400, 400));
        primaryStage.show();
    }

    public static class PlotPane extends Pane {
        private final BooleanProperty active = new SimpleBooleanProperty();
        public BooleanProperty activeProperty() {
            return active ;
        }
        public final boolean isActive() {
            return activeProperty().get();
        }
        public final void setActive(boolean active) {
            activeProperty().set(active);
        }

        public PlotPane(String name) {
            // just for demo...
            activeProperty().addListener((obs, wasActive, isActive) -> {
                if (isActive) {
                    System.out.println(name + " is active");
                } else {
                    System.out.println(name + " is inactive");
                }
            });
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

你说得对 canvas 如果物理上不可见就不会被绘制,但这给了你机会 on/off 数据流监控等

如果需要,您可以进一步优化绑定,例如

plot1.activeProperty().bind(tabPane.getSelectionModel().selectedItemProperty().isEqualTo(tab1)
    .and(tabPane.sceneProperty().isNotNull()));