JavaFX:如何让 setCellValueFactory 忽略 ObservableArray 中的空项?

JavaFX: How can you get setCellValueFactory to ignore empty items in an ObservableArray?

我是java的新手,对Javafx的结构还不是很了解,所以在这里杂草丛生。

这是我正在学习的课程的库存管理应用程序。

我有两个继承自抽象 class 的 classes 和一个包含 child classes 实例的 ObservableList。现在,当我尝试在带有 cellvaluefactory 的 TableView 中同时显示两个 class 时,由于其中一个或另一个缺少属性,我会收到错误消息。该程序编译但警告是由于字段 "missing" 来自其各自实例中缺少的字段。 (PartOutsource 缺少 "machineId" 字段,PartInhouse 缺少 "companyName" 字段。

UI picture

我知道一定有某种方法可以让 cellValueFactory 忽略空值或用空字符串填充它们,但我不知道如何,google 一直是死胡同。

这是两个 child classes:

public class PartOutsourced extends Part {
    private String companyName;

    public PartOutsourced(String name, int stock, double price, int min, int max, String companyName) {
        super(name, stock, price, min, max);
        this.companyName = companyName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }
}
public class PartInHouse extends Part {
    private int machineId;

    public PartInHouse(String name, int stock, double price, int min, int max, int machineId) {
        super(name, stock, price, min, max);
        this.machineId = machineId;
    }

    public int getMachineId() {
        return machineId;
    }

    public void setMachineId(int machineId) {
        this.machineId = machineId;
    }
}

这里是 cellValueFactory 代码:

        col_id.setCellValueFactory(new PropertyValueFactory<Part, String>("id"));
        col_name.setCellValueFactory(new PropertyValueFactory<Part, String>("name"));
        col_stock.setCellValueFactory(new PropertyValueFactory<Part, String>("stock"));
        col_price.setCellValueFactory(new PropertyValueFactory<Part, String>("price"));
        col_min.setCellValueFactory(new PropertyValueFactory<Part, String>("min"));
        col_max.setCellValueFactory(new PropertyValueFactory<Part, String>("max"));
        col_machine_id.setCellValueFactory(new PropertyValueFactory<Part, String>("machineId"));
        col_company.setCellValueFactory(new PropertyValueFactory<Part, String>("companyName"));

一点堆栈跟踪:

May 14, 2020 12:14:06 PM javafx.scene.control.cell.PropertyValueFactory getCellDataReflectively
WARNING: Can not retrieve property 'machineId' in PropertyValueFactory: javafx.scene.control.cell.PropertyValueFactory@5f88bef3 with provided class type: class inventory.datamodel.PartOutsourced
java.lang.IllegalStateException: Cannot read from unreadable property machineId

使用您自己的 Callback 实现(例如使用 lambda)而不是 PropertyValueFactory(无论如何通常推荐这样做;PropertyValueFactory 实际上只是作为 lambda 之前的占位符编写的引入了表达式,因为使用内部 class 实现 Callback 非常冗长)。然后你可以 return 你需要的 属性 (例如 IntegerProperty 包装值)如果对象是正确的类型,或者 属性 包装 null (例如 ObjectProperty<Number>) 如果没有。

因为你的 machineIdNumber,你应该使用

private TableColumn<Part, Number> col_machine_id ; // note: please use proper Java naming conventions

然后你可以做

col_machine_id.setCellValueFactory(cellData -> {
    Part part = cellData.getValue() ;
    if (part instanceof PartInHouse) {
        return new SimpleIntegerProperty(((PartInHouse)part).getMachineId());
    }
    return new SimpleObjectProperty<>(null);
});

请注意,还建议在您的模型中使用 JavaFX 属性 class:

public class PartInHouse extends Part {
    private final IntegerProperty machineId = new SimpleIntegerProperty();

    public PartInHouse(String name, int stock, double price, int min, int max, int machineId) {
        super(name, stock, price, min, max);
        setMachineId(machineId);
    }

    public IntegerProperty machineIdProperty() {
        return machineId ;
    }

    public final int getMachineId() {
        return machineIdProperty().get();
    }

    public final void setMachineId(int machineId) {
        machineIdProperty().set(machineId);
    }
}

在这种情况下,您可以使用以下单元格值工厂:

col_machine_id.setCellValueFactory(cellData -> {
    Part part = cellData.getValue() ;
    if (part instanceof PartInHouse) {
        return ((PartInHouse)part).machineIdProperty();
    }
    return new SimpleObjectProperty<>(null);
});

这是一个完整的 运行 示例:

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class InheritanceTableModelTest extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        TableView<Part> table = new TableView<>();
        for (int i = 1 ; i <= 20 ; i++) {
            table.getItems().add(new PartInHouse("Part "+(2*i), i));
            table.getItems().add(new PartOutsourced("Part "+(2*i+1), "Company "+i));
        }
        TableColumn<Part, String> nameCol = new TableColumn<>("Part");
        TableColumn<Part, Number> machineIdCol = new TableColumn<>("Machine ID");
        TableColumn<Part, String> companyCol = new TableColumn<>("Company");

        nameCol.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
        machineIdCol.setCellValueFactory(cellData -> {
            Part part = cellData.getValue();
            if (part instanceof PartInHouse) {
                return ((PartInHouse)part).machineIdProperty();
            }
            return new SimpleObjectProperty<>(null);
        });
        companyCol.setCellValueFactory(cellData -> {
            Part part = cellData.getValue();
            if (part instanceof PartOutsourced) {
                return ((PartOutsourced)part).companyNameProperty();
            }
            return new SimpleObjectProperty<>(null);
        });

        table.getColumns().add(nameCol);
        table.getColumns().add(machineIdCol);
        table.getColumns().add(companyCol);

        Scene scene = new Scene(new BorderPane(table));
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static class Part {
        private final StringProperty name = new SimpleStringProperty();
        public Part(String name) {
            setName(name);
        }
        public StringProperty nameProperty() {
            return name;
        }
        public final String getName() {
            return nameProperty().get();
        }
        public final void setName(String name) {
            nameProperty().set(name);
        }
    }

    public static class PartInHouse extends Part {
        private final IntegerProperty machineId = new SimpleIntegerProperty() ;
        public PartInHouse(String name, int machineId) {
            super(name);
            setMachineId(machineId);
        }
        public IntegerProperty machineIdProperty() {
            return machineId ;
        }
        public int getMachineId() {
            return machineIdProperty().get();
        }
        public void setMachineId(int machineId) {
            machineIdProperty().set(machineId);
        }
    }

    public static class PartOutsourced extends Part {
        private final StringProperty companyName = new SimpleStringProperty();

        public PartOutsourced(String name, String companyName) {
            super(name);
            setCompanyName(companyName);
        }

        public StringProperty companyNameProperty() {
            return companyName ;
        }

        public String getCompanyName() {
            return companyNameProperty().get();
        }

        public void setCompanyName(String companyName) {
            companyNameProperty().set(companyName);
        }
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

}