Javafx 2 JavaFX画布-仅当画布可见时绘制

Javafx 2 JavaFX画布-仅当画布可见时绘制,javafx-2,javafx-8,Javafx 2,Javafx 8,我正在考虑将我们的内部自动/测量应用程序从Swing移植到JavaFX,主要是因为它具有更好的外观和多点触控支持,但我找不到一种方法来仅在自定义组件可见时渲染它们 为了激发动机,想象一个有10个选项卡的屏幕,每个选项卡内都有一个显示正在测量的一些实时数据的绘图。在任何时候,只能看到一个情节。数据量很大,计算机有足够的能力一次绘制一幅图,但不能同时绘制所有图 摇摆版本 现在在Swing版本中,这种行为的实现非常简单。每个绘图都是一个自定义JComponent,它具有覆盖的paintComponen

我正在考虑将我们的内部自动/测量应用程序从Swing移植到JavaFX,主要是因为它具有更好的外观和多点触控支持,但我找不到一种方法来仅在自定义组件可见时渲染它们

为了激发动机,想象一个有10个选项卡的屏幕,每个选项卡内都有一个显示正在测量的一些实时数据的绘图。在任何时候,只能看到一个情节。数据量很大,计算机有足够的能力一次绘制一幅图,但不能同时绘制所有图

摇摆版本

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

  • 当组件可见时,例如,用户选择带有绘图的选项卡
  • 当实时数据更改时,我的代码会对组件调用repaint(),如果该组件可见(或者当该组件再次可见时),则会调用paintComponent
它比这更复杂(详细信息请参见),但其工作原理大致如下。重要的是,只有可见的绘图也在被处理和重画

移植到JavaFX?

现在,我尝试将这种行为移植到JavaFX,画布是一种自然选择。但是paintComponent回调机制消失了,您只需直接绘制到Canvas.getGraphicsContext2D

如果我理解正确的话,在这个上下文中调用draw操作并不会真正执行任何即时绘制,它只是将操作推送到堆栈上,当Prism线程运行时,Prism子系统会处理这个堆栈(这正确吗?)。因此,如果Prism发现画布不可见(选择了另一个选项卡,它被另一个节点遮挡,位于屏幕的可见部分之外,…),那么可能(我只是猜测,我对JavaFX非常陌生),根本不会有实际的绘制,对吗?那太好了

但即使是这种情况,它也帮不了我,因为我甚至不想开始渲染,直到我知道画布是可见的。即使是对实时数据进行预处理以进行绘图,也可能需要相当多的CPU能力,甚至可能比渲染本身还要多——想象一下,插值数千个数据点以绘制几行。因此,在实时数据更改之后,我想首先测试每个绘图(画布)的实际可见性,然后再开始重新绘制。当变得可见时,还需要通知我。这在JavaFX中是可能的吗?还是说我在画布上走了一条糟糕的路线,还有更好的方法


请注意,不要将此可见性与Node.visibleProperty混淆。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");
            }
        });
    }
}
然后,当您将其组装到选项卡窗格中时,可以绑定属性:

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);
    }
}
如果画布在物理上不可见,则不会对其进行绘制,这是正确的,但这为您提供了打开/关闭数据流监视等功能的机会

如果需要,可以进一步细化绑定,例如:

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

好吧,这是个令人伤心的消息,在JavaFX中,在Swing中自动工作的东西基本上是不可能的。无论如何,非常感谢您为这个问题提供了一个干净的手动解决方案。如果存在类似于
Node.isPhysicallyVisibleProperty
的东西,它实际上可能工作得很好。。我会再等一会儿,然后把你的帖子标记为回答,谢谢。我不确定我是否理解你为什么认为这是不可能的:你肯定只是按照我显示的创建属性,然后观察它,根据需要打开/关闭处理?这不管用吗?问题是您的解决方案仅限于选项卡窗格中的画布,我必须使用您的解决方案手动连接画布。当窗口最小化时,它不会停止预处理/渲染;当它处于手风琴中时,它不会停止打印;当它被遮挡时,它不会停止预处理/渲染;当它由于任何其他无法想象的原因而无法在物理上看到时,它也不会停止预处理/渲染。没有任何进一步效果的Swing解决方案不会渲染绘图,除非它在物理上可见。