PropertyValueFactory 是一个没有人应该使用的类。它依赖于反射,这意味着如果你犯了错误,编译器无法通知你你的错误。它还有一个不愉快的习惯,就是默默地吞下任何异常,这使得错误更难被发现。
A cell value factory is a Callback,在功能上与 Java SE Function 完全相同。任何接受TableColumn.CellDataFeatures 作为参数并返回对应于该单元格中table item 的ObservableValue 的方法或lambda,都可以充当单元格值工厂。
所以,最好的办法是忘记 PropertyValueFactory,而是传递一个简单的 lambda。
做到这一点的最佳方法是确保每个数据对象的属性都采用 JavaFX 属性的样式。如果您曾经看过 documentation for Node 或 Window 或 JavaFX 中的大多数其他类,您可能已经注意到,虽然普通 JavaBean 的每个属性都有两个方法(get-method 和 set-method ),一个JavaFX对象通常有三个方法:
- get 方法
- 一套方法
- 一种属性访问器方法,它返回Property 本身,而不是属性值
一些例子:
你会想要在你的数据对象中做同样的事情。
为了清楚起见,我将使用 Person 和 Address 而不是 A、B 和 C:
public class Address {
private final StringProperty street = new SimpleStringProperty();
private final StringProperty postalCode = new SimpleStringProperty();
public StringProperty streetProperty() {
return street;
}
public String getStreet() {
return street.get();
}
public void setStreet(String newStreet) {
this.street.set(newStreet);
}
public StringProperty postalCodeProperty() {
return postalCode;
}
public String getPostalCode() {
return postalCode.get();
}
public void setPostalCode(String code) {
this.postalCode.set(code);
}
}
public class Person {
private final StringProperty name = new SimpleStringProperty();
private final ObjectProperty<LocalDate> dateOfBirth = new SimpleObjectProperty<>();
private final ObjectProperty<Address> address = new SimpleObjectProperty<>();
public StringProperty nameProperty() {
return name;
}
public String getName() {
return name.get();
}
public void setName(String newName) {
this.name.set(newName);
}
public ObjectProperty<LocalDate> dateOfBirthProperty() {
return dateOfBirth;
}
public LocalDate getDateOfBirth() {
return dateOfBirth.get();
}
public void setDateOfBirth(LocalDate date) {
this.dateOfBirth.set(date);
}
public ObjectProperty<Address> addressProperty() {
return address;
}
public Address getAddress() {
return address.get();
}
public void setAddress(Address newAddress) {
this.address.set(newAddress);
}
}
每个属性都实现了 ObservableValue,所以现在你的单元格数据值可以是属性对象本身:
TableColumn<Person, String> nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(cell -> cell.getValue().nameProperty());
由于上面的代码没有使用反射,编译器可以保证你显示的是一个合法的值。
为了解决你原来的问题,如果你想显示一个子对象的值,你可以使用Bindings的select*方法之一:
TableColumn<Person, String> streetColumn = new TableColumn<>();
streetColumn.setCellValueFactory(
cell -> Bindings.selectString(cell.getValue().addressProperty(), "street"));
这确实有使用反射的缺点(虽然there is an open bug about that),但至少父对象被编译器检查了。