JavaFX - 为什么将节点多次添加到窗格或不同的窗格会导致错误?

Hyd*_*yde 5 java javafx javafx-2

我现在正在学习基本的 JavaFX,但我不明白我正在阅读的书中的这句话:“不,像文本字段这样的节点只能添加到一个窗格中一次。将节点添加到窗格多次或不同的窗格将导致运行时错误。” 我可以从书中提供的 UML 图中看到它是一个组合,但我不明白为什么(库类代码实现)是这样。

例如,为什么这会导致编译错误?不是在窗格中实例化了一个新的文本字段,因为它是一个组合吗?

FlowPane pane = new FlowPane();
StackPane pane2 = new StackPane();
TextField tf = new TextField();
pane.getChildren().add(tf);
pane.getChildren().add(tf);
Run Code Online (Sandbox Code Playgroud)

另外,为什么以下运行但不显示放置在窗格中的文本字段?

FlowPane pane = new FlowPane();
StackPane pane2 = new StackPane();
TextField tf = new TextField();
pane.getChildren().add(tf);
pane2.getChildren().add(tf);

primaryStage.setScene(new Scene(pane));
primaryStage.show();
Run Code Online (Sandbox Code Playgroud)

Jam*_*s_D 6

这基本上是 API 设计方式的(故意)结果。每个Node都有一组属性,包括一个parent属性(场景图中节点的唯一一个父节点),以及诸如layoutX和 之类的属性,layoutY它们是节点相对于其父节点的坐标。因此,一个节点只能属于一个父节点,并且只能添加到父节点一次(因为它在父节点中只能有一个位置)。以这种方式组织事物可以实现非常有效的布局过程。

另一种思考方式:假设你的第一个代码块做了你想要的;所以文本字段tf在流窗格中出现了两次。你希望得到什么结果tf.getBoundsInParent()?由于tf在父级中出现两次,API 将无法为此调用提供合理的值。

您在问题中的陈述有几个不准确之处:

例如,为什么这会导致编译错误?不是在窗格中实例化了一个新的文本字段,因为它是一个组合吗?

首先,从技术上讲,这是聚合,而不是组合;尽管我不确定理解差异是否有助于您了解此时正在发生的事情。

其次,这里没有编译错误;您在运行时收到错误(pane检测到相同的node已添加两次;编译器无法检查)。

第三,父节点不会实例化您添加到它们的节点的副本。如果是这样,您将无法更改显示的节点的属性。例如,如果FlowPane在您的示例中,TextField当您调用 时实例化了一个 new pane.getChildren().add(tf);,然后显示该新文本字段,那么如果您随后调用tf.setText("new text"),它将没有效果,因为它不会更改pane正在显示的文本字段的文本.

当您调用时,pane.getChildren().add(...)您会传递一个对要添加的节点的引用;该节点随后显示为窗格的子节点。任何其他实现都会产生非常违反直觉的行为。

在您的第二个代码块中:

pane.getChildren().add(tf);
pane2.getChildren().add(tf);
Run Code Online (Sandbox Code Playgroud)

第二个调用隐式地设置了to的parent属性;因此不再是 的孩子。所以这段代码具有从第一个父级中删除的效果,. 据我所知,这种副作用没有记录在案,因此您可能应该避免编写这样的代码。tfpane2tfpanetfpane


tim*_*tim 0

尝试这个:

TextField tf = new TextField();
TextField tf2 = new TextField();
pane.getChildren().add(tf);
pane.getChildren().add(tf2);
Run Code Online (Sandbox Code Playgroud)

同一节点不能添加两次的原因是,GUI 中只能看到一个具有相同规格和尺寸的节点。这就像将相同的蓝色圆圈复制到原始蓝色圆圈上。对于用户来说,它看起来是一样的,但它占用更多的内存。