Javafx IllegalArgumentException:儿童:添加了重复的儿童

Javafx IllegalArgumentException: Children : Duplicate children added

我似乎无法弄清楚为什么即使我只向 table 添加了一个内容也会显示重复的子项添加错误。

有 3 个主要 classes :

  1. WindowTease:加载舞台并调用 loadTable() 方法
  2. InventoryController:标准的 fxml 控制器,包含通用的 loadTable() 方法
  3. InventoryCell:我用它来为每一列设置 cellfactory()。

    原因:java.lang.IllegalArgumentException:子项:添加了重复的子项:parent = TableRow@6c41701f[styleClass=cell indexed-cell table-row-cell]'null'

这是我的控制器:

public class InventoryController {

    @FXML protected TableView mainTable;

  public <U> void loadTable(TableCell<U, Component> cellFactory){

    mainTable.getColumns().clear();

    final String[] propertyName = {"id", "invCategory", "quantity", "description", "perItem", "icon"};
    final String[] columnName = {"ID", "Category", "Quantity", "Description", "Price (Per Item)", "Process"};

    for (int i = 0; i < propertyName.length; i++) {
        TableColumn<U, Component> column = new TableColumn<>(columnName[i]);
        column.setCellValueFactory(new PropertyValueFactory<>(propertyName[i]));
        column.setCellFactory(param -> cellFactory);    //this is the culprit
        //column.setCellFactory(param -> new InventoryCell());   //this shows with no problem
        mainTable.getColumns().add(column);
    }

    ObservableList<Inventory> items = FXCollections.observableArrayList();
    mainTable.getItems().clear();

    for (int i = 0; i < 1; i++) {
        Inventory inve = new Inventory(
                new ID("WSS", i), new Describer("Click me !!"),
                new PercentQuantity(i, 100), new Describer("Click me !!"), new Price(Currency.CHINESE_YEN, i*1000.00),
                new HoverIcon("images/assignment_returned.png"));
        mainTable.getItems().add(inve);
    }

}

这是应用程序 class:

public class WindowTease extends Application {

    @Override
    private void start(Stage primaryStage) throws Exception {
    FXMLLoader loader = new FXMLLoader());
    loader.setLocation(WindowTease.class.getResource("/layouts/inventory.fxml"));
    InventoryController controller = new InventoryController();
    loader.setController(controller);   
    Parent root = loader.load();
    Scene scene = new Scene(root);

    controller.loadTable(new InventoryCell());

    primaryStage.setScene(scene);
    primaryStage.show();            //Caused by: java.lang.RuntimeException: Exception in Application start method
}

最后,InventoryCell 扩展了 TableCell:

public class InventoryCell extends TableCell<Inventory, Component>{

    @Override
    protected void updateItem(Component item, boolean empty) {
        if (item == null || empty) return;
        super.updateItem(item, empty);
        Object node = item.getNode();
        if (node instanceof Node) {
            Node graphix = ((Node)node);
            HBox box = new HBox(graphix);   
            setText("");
            setGraphic(box);

        } else if (node instanceof String) {
            setText((String)node);
            setGraphic(null); 
        }
    }
}

更新: 罪魁祸首绝对是tablecolumn.setCellFactory(cellfactory);

它被称为 cellFactory,而不是 cellContainer,原因是:

TableView 使用它的 TableColumncellFactory 创建节点来显示列的数据。对显示的每一行执行一次。

如果您现在 return 每次都使用相同的 TableCell 实例,稍后当 TableViewSkin 最终组装布局时,某些情况如这最终将达到:

SomeParent
    |
    |--TableRow1 
    |      |
    |      |--InventoryCell1
    |
    |--TableRow2
           |
           |--InventoryCell1

这是不允许的,因为 InventoryCell1 不能在场景图中包含多次。

因此,您必须确保为工厂的每次调用 return 编辑不同的 TableCell 实例。

param -> cellFactory

将只是 return TableCell 的实例,它被一遍又一遍地传递给 loadTable 方法。

使用方法 java 8 个引用你可以很容易地创建一个工厂但是:

public <U> void loadTable(Supplier<TableCell<U, Component>> cellFactory){
    ...
    column.setCellFactory(param -> cellFactory.get());
controller.loadTable(InventoryCell::new);