我正在尝试修改我正在运行的应用程序的 Java 代码中通过 FXML 文件加载的 JavaFX 场景。
我有以下使用 SceneBuilder 创建的测试 FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.shape.Line?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="720.0" prefWidth="1280.0" xmlns="" xmlns:fx="">
<top>
<TabPane prefHeight="100.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
<tabs>
<Tab text="Untitled Tab 1">
<content>
<AnchorPane prefHeight="100.0" prefWidth="200.0" />
</content>
</Tab>
<Tab text="Untitled Tab 2">
<content>
<AnchorPane prefHeight="100.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>
</top>
<center>
</center>
</BorderPane>
这只是一个
BorderPane
,其中 Top
部分有两个空选项卡,还有一个空的 Center
部分。我希望能够在Center
部分绘制一些形状。我使用 以下示例代码 在 Canvas
窗口中绘制一些随机形状,并确保示例代码本身可以工作:
然后,我想将完全相同的绘制的
Canvas
(具有完全相同的形状)插入到上面显示的 FXML 文件中声明的 Center
的 BorderPane
部分中。
但是,我对如何正确引用 FXML 加载器加载的场景的各个子项有点困惑。如果我在我的
start
函数中尝试以下代码:
public void start(Stage stage) throws IOException
{
stage.setTitle("Drawing Operations Test");
Scene scene = new Scene(loadFXML("primary"), 1280, 720);
Canvas canvas = new Canvas(300, 250);
GraphicsContext gc = canvas.getGraphicsContext2D();
drawShapes(gc);
scene.getRoot().getChildrenUnmodifiable().get(0).getChildren().add(canvas);
stage.setScene(scene);
stage.show();
}
其中
drawShapes
是在Canvas
上绘制形状的函数,线:
scene.getRoot().getChildrenUnmodifiable().get(0).getChildren().add(canvas);
被列为错误,因为在
getChildren
类中找不到类函数 Node
。
现在,我对该错误原因的最初假设是因为我应该将
Node
对象类型转换到我想要添加 canvas
的相应类中,在我的例子中是 BorderPane
类。然而,我想澄清 getChildren
类函数将在这里返回给我什么。
getChildren
是否返回 BorderPane
对象,因为它是 FXML 文件中声明的第一个对象?或者该元素是否是其他元素的父元素?将我的 Canvas
添加到我的 Center
的 BorderPane
部分的正确方法是什么?
感谢您阅读我的帖子,感谢任何指导。
scene.getRoot()
将返回场景的根,您可以通过调用
Scene
构造函数来设置该根:
Scene scene = new Scene(loadFXML("primary"), 1280, 720);
换句话说,
scene.getRoot()
是从load.FXML(...)
返回的对象。您没有向我们展示该方法定义,因此我们无法确定,但可以合理地假设它返回调用 FXMLLoader.load(...)
的结果,这将是 BorderPane
(根元素,在 XML 意义上, FXML 文件的一部分)。
因此,您可以将
scene.getRoot()
的结果转换为 BorderPane
,然后调用 setCenter(...)
(或者,显然更好,首先保留对 loadFXML(...)
结果的引用)。
代码
scene.getRoot().getChildrenUnmodifiable()
将返回
BorderPane
的子节点列表,即仅包含 TabPane
的列表,所以
scene.getRoot().getChildrenUnmodifiable().get(0)
是对
TabPane
的引用,并且
scene.getRoot().getChildrenUnmodifiable().get(0).getChildren()
(添加了适当的转换)是
TabPane
的子节点列表。该列表的内容基本上是 TabPane
及其皮肤的实现细节。
但这都不是 API 设计者想要使用的方式。相反,为 FXML 定义一个控制器类,并将
BorderPane
注入其中:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.shape.Line?>
<BorderPane
maxHeight="-Infinity" maxWidth="-Infinity"
minHeight="-Infinity" minWidth="-Infinity"
prefHeight="720.0" prefWidth="1280.0"
xmlns="" xmlns:fx=""
fx:controller="my.package.MyController"
fx:id="borderPane"
>
<!-- ... -->
</BorderPane>
然后定义控制器,并在
initialize()
方法中修改边框窗格(如果您想立即添加画布),或在事件处理程序中(如果您想修改场景图以响应用户操作):
package my.package;
// imports ...
public class MyController {
@FXML
private BorderPane borderPane;
@FXML
private void initialize() {
Canvas canvas = new Canvas(300, 250);
GraphicsContext gc = canvas.getGraphicsContext2D();
drawShapes(gc);
borderPane.setCenter(canvas);
}
private void drawShapes(GraphicsContext gc) {
// ...
}
}