JavaFX canvas - 仅在 canvas 可见时绘制
JavaFX canvas - draw only when the canvas is visible
我正在考虑将我们的内部 automotive/measuring 应用程序从 Swing 移植到 JavaFX,主要是因为更好的外观和多点触控支持,但我找不到仅在自定义组件可见时呈现它们的方法.
出于动机,想象一个有 10 个选项卡的屏幕,每个选项卡内都有一个图表,显示一些正在测量的实时数据。任何时候,只能看到一个情节。数据量大,计算机有足够的能力一次渲染一个图,但不能同时全部绘制。
摇摆版
现在在 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 混淆,后者做了一些不同的事情,基本上关闭了节点的渲染。
使用选项卡窗格,我认为唯一的方法是观察选项卡窗格的选定项(即选定的选项卡)。您可能想对您的绘图区域进行编码,使其独立于它是否显示在选项卡窗格中,因此我会在绘图中包含一个 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()));
我正在考虑将我们的内部 automotive/measuring 应用程序从 Swing 移植到 JavaFX,主要是因为更好的外观和多点触控支持,但我找不到仅在自定义组件可见时呈现它们的方法.
出于动机,想象一个有 10 个选项卡的屏幕,每个选项卡内都有一个图表,显示一些正在测量的实时数据。任何时候,只能看到一个情节。数据量大,计算机有足够的能力一次渲染一个图,但不能同时全部绘制。
摇摆版
现在在 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 混淆,后者做了一些不同的事情,基本上关闭了节点的渲染。
使用选项卡窗格,我认为唯一的方法是观察选项卡窗格的选定项(即选定的选项卡)。您可能想对您的绘图区域进行编码,使其独立于它是否显示在选项卡窗格中,因此我会在绘图中包含一个 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()));