JavaFX画布 - 仅在画布可见时绘制

时间:2015-10-15 10:53:18

标签: javafx-2 javafx-8

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

为了激励,想象一个带有10个标签的屏幕,每个标签内部是一个显示正在测量的实时数据的图表。在任何时候,只能看到一个情节。数据量很大,计算机有足够的能力一次渲染一个绘图,但不能同时渲染所有绘图。

Swing版本

现在在Swing版本中,这种行为的实现非常简单。每个绘图都是一个自定义 JComponent ,带有覆盖 paintComponent 方法,可以绘制所有绘图。 paintComponent 在两个重要案例中被调用:

  • 当组件变得可见时,例如用户选择带有情节
  • 的选项卡
  • 当实时数据发生变化时,我的代码会调用组件上的 repaint(),这会导致 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 混淆,一个人做了不同的事情,基本上关闭了节点的渲染。

1 个答案:

答案 0 :(得分:3)

使用选项卡窗格,我认为唯一的方法是观察选项卡窗格的选定项目(即选定的选项卡)。您可能希望对绘图区域进行编码,因此它与是否显示在选项卡窗格中无关,因此我会在绘图中包含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()));