Javafx 将动态窗格添加到 vbox Duplicate Children 错误

Javafx adding dynamically pane to vbox Duplicate Children error

我在 fxml 文件的 VBox 中有一个带有标签、文本字段和组合框的窗格。我们称它为 tempPane。 在同一阶段,我有一个按钮。 按下按钮后,我需要向 VBox 添加一个与 tempPane 完全相同的窗格。即,向 VBOX 动态添加一个窗格。 我能够向 VBox 添加单独的控件,例如按钮或标签或文本字段,但是在尝试添加这个新窗格时我无法获得相同的结果。

部分控制器代码:

@FXML
private Pane tempPane;

@FXML 
private Button btnAddNewPane;;

@FXML
private VBox vBox;

@FXML
void addNewPane(ActionEvent event) {

    ...
        Pane newPane = new Pane();
        newPane = tempPane;
        // New ID is set to the newPane, this String (NewID) should be 
        //different each time button is pressed
        newPane.setId(newID);
        vBox.getChildren().add(newPane);
    ...
}

我得到的错误是:

Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Children: duplicate children added: parent = VBox[id=filterBox]
at javafx.graphics/javafx.scene.Parent.onProposedChange(Parent.java:580)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:206)
at com.sener.dbgui.controller.SearchController.run(SearchController.java:53)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:418)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:417)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop(WinApplication.java:175)
at java.base/java.lang.Thread.run(Thread.java:844)

那么,为什么我会收到重复的子项错误?我在将它添加到 VBox 之前更改了 newPane ID。

您的评论中已经写明了您获得重复 ID 的原因。

// New ID is set to the newPane, this String (NewID) should be

//different each time button is pressed

您正在传递相同的字符串作为参数。

newPane.setId("NewID");

尝试为每个窗格使用动态生成的唯一 ID。

String newId; //generate the id by user input or internally
    newPane.setId(newId);
Pane newPane = new Pane();
newPane = tempPane;
...
vBox.getChildren().add(newPane);

此代码确实创建了一个新的 Pane(第一行),但通过用旧实例覆盖它(第二行)立即删除了新实例。

发生错误是因为 Node 的合同不允许它在一个场景中放置两次,而您添加的 Pane 已经是 [=18= 的子项了] 再次。修改 id 属性 不会改变这一事实。

如果这应该有效,您需要创建一个以 tempPane 为根的子场景的新副本。

您可以为这个场景创建自定义 Pane

subFXML.fxml

<fx:root xmlns:fx="http://javafx.com/fxml" type="javafx.scene.layout.Pane">
    <!-- content of tempPane from old fxml goes here -->
    ...
    <Button fx:id="btnAddNewPane" />
    ...
</fx:root>
public class MyPane extends Pane {

    public MyPane() {
        FXMLLoader loader = getClass().getResource("subFXML.fxml");
        loader.setRoot(this);
        loader.setController(this);

        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    @FXML
    private Button btnAddNewPane;

    public void setOnAction(EventHandler<ActionEvent> handler) {
        btnAddNewPane.setOnAction(handler);
    }

    public EventHandler<ActionEvent> getOnAction() {
        return btnAddNewPane.getOnAction();
    }
}

旧的 fxml

一定要导入 MyPane。

...
<VBox fx:id="vBox">
    <children>
        <!-- replace tempPane with MyPane -->
        <MyPane onAction="#addNewPane"/>
    </children>
</VBox>
...

旧控制器

@FXML
private VBox vBox;

@FXML
void addNewPane(ActionEvent event) {

    ...
        MyPane newPane = new MyPane();
        newPane.setId(newID); // Don't know why setting the CSS id is necessary here
        newPane.setOnAction(this::addNewPane); // set onAction property
        vBox.getChildren().add(newPane);
    ...
}