问题描述
public class C{
B b;
A a;
int d;
String str;
}
A 类和 B 类有自己的变量。
我有这样的代码,我想在 TableView 中使用 C 类,如何访问 A 类和 B 类中的值?我必须在 column..setCellValueFactory(new PropertyValueFactory<>("???"));
和 TableColumn<???,String> column = new TableColumn<>("Example");
解决方法
PropertyValueFactory 是一个没人应该使用的类。它依赖于反射,这意味着如果你犯了一个错误,编译器不能通知你你的错误。它还具有默默吞下任何异常的不愉快习惯,这使得错误更难以检测。
A cell value factory is a Callback,在功能上与 Java SE Function 完全相同。任何接受 TableColumn.CellDataFeatures 作为参数并返回对应于该单元格中的 table item 的 ObservableValue 的方法或 lambda 都可以充当单元格值工厂。
因此,最好的做法是忘记 PropertyValueFactory,而是传递一个简单的 lambda。
做到这一点的最佳方法是确保您的每个数据对象的属性都采用 JavaFX 属性的样式。如果您曾经看过 JavaFX 中的 documentation for Node 或 Window 或大多数其他类,您可能已经注意到,虽然普通的 JavaBean 对每个属性都有两个方法(get-method 和set-method),一个JavaFX对象通常有3个方法:
- 获取方法
- 设置方法
- 一个属性访问器方法,它返回 Property 本身,而不是属性值
一些例子:
- getBlendMode、setBlendMode、blendModeProperty
- isVisible、setVisible、visibleProperty
- getOpacity、setOpacity、opacityProperty
您将希望在数据对象中做同样的事情。
为了清楚起见,我将使用 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),但至少父对象会被编译器检查。