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

Laz*_*tle 1 children javafx duplicates 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);
    ...
}
Run Code Online (Sandbox Code Playgroud)

我得到的错误是:

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)
Run Code Online (Sandbox Code Playgroud)

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

fab*_*ian 5

Pane newPane = new Pane();
newPane = tempPane;
...
vBox.getChildren().add(newPane);
Run Code Online (Sandbox Code Playgroud)

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

发生错误是因为 的合同Node不允许它在一个场景中放置两次,并且您正在添加Pane已经是 的孩子的同一个vBox。修改id属性不会改变这一事实。

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

您可以Pane为此场景创建自定义:

子FXML文件

Pane newPane = new Pane();
newPane = tempPane;
...
vBox.getChildren().add(newPane);
Run Code Online (Sandbox Code Playgroud)
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();
    }
}
Run Code Online (Sandbox Code Playgroud)

旧文件

请务必导入 MyPane。

<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>
Run Code Online (Sandbox Code Playgroud)

旧控制器

@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);
    ...
}
Run Code Online (Sandbox Code Playgroud)