代码之家  ›  专栏  ›  技术社区  ›  LazyTurtle

Javafx向vbox动态添加窗格复制子项错误

  •  1
  • LazyTurtle  · 技术社区  · 7 年前

    我在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 回复  |  直到 7 年前
        1
  •  5
  •   fabian    7 年前
    Pane newPane = new Pane();
    newPane = tempPane;
    ...
    vBox.getChildren().add(newPane);
    

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

    错误发生于 Node 不允许在场景中放置两次,并且您正在添加相同的 窗玻璃 那已经是 vBox 再一次修改 id 属性不会改变这一事实。

    您需要创建一个新的子场景副本,其根位于 tempPane 如果这是可行的。

    您可以创建自定义 窗玻璃 对于此场景:

    子XML。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);
        ...
    }
    
        2
  •  0
  •   Community CDub    5 年前

    它已经写在你的评论中,为什么你会得到重复的ID。

    //新ID设置为newPane,此字符串(NewID)应为

    //每次按下按钮都不同

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

    newPane.setId("NewID");
    

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

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