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

时间:2018-01-22 09:17:33

标签: javafx duplicates children pane vbox

我在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$3.onProposedChange(Parent.java:580)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:206)
at com.sener.dbgui.controller.SearchController$1.run(SearchController.java:53)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(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$3(WinApplication.java:175)
at java.base/java.lang.Thread.run(Thread.java:844)

那么,为什么我得到这个重复的孩子错误?我在更新newPane ID之前将其添加到VBox。

2 个答案:

答案 0 :(得分:3)

Pane newPane = new Pane();
newPane = tempPane;
...
vBox.getChildren().add(newPane);

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

错误发生,因为Node的合同不允许在场景中放置两次,而您又添加了Pane已经是{{1}的孩子的vBox再次。修改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);
    ...
}

答案 1 :(得分:0)

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

  

//新ID设置为newPane,此String(NewID)应为

     每次按下按钮

//不同

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

newPane.setId("NewID");

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

String newId; //generate the id by user input or internally
    newPane.setId(newId);