我正在考虑将我们的内部汽车/测量应用程序从Swing移植到JavaFX,主要是因为它具有更好的外观和多点触控支持,但我找不到一种只在可见的时候呈现自定义组件的方法。
为了激励,想象一个带有10个标签的屏幕,每个标签内部是一个显示正在测量的实时数据的图表。在任何时候,只能看到一个情节。数据量很大,计算机有足够的能力一次渲染一个绘图,但不能同时渲染所有绘图。
Swing版本
现在在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 混淆,一个人做了不同的事情,基本上关闭了节点的渲染。
答案 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()));