Solution
使用 lambda 单元格值工厂而不是PropertyValueFactory https://openjfx.io/javadoc/20/javafx.controls/javafx/scene/control/cell/PropertyValueFactory.html.
有关两者之间差异的一些解释,请参阅:
- Java: setCellValuefactory; Lambda 与 PropertyValueFactory;优点缺点 https://stackoverflow.com/questions/38049734/java-setcellvaluefactory-lambda-vs-propertyvaluefactory-advantages-disadvant
为什么这有效
正如您所指出的,问题在于记录访问器不遵循标准JavaBean https://en.wikipedia.org/wiki/JavaBeans属性命名约定,这就是PropertyValueFactory
期望。例如,一条记录使用first()
而不是getFirst()
作为访问器,这使得它与PropertyValueFactory
.
如果您应用“做这件可怕的事情”的解决方法,向记录中添加额外的 get 方法,这样您就可以利用PropertyValueFactory
与一个接口TableView
? -> 绝对不, 有一个更好的办法 :-)
解决这个问题需要定义您自己的自定义单元工厂,而不是使用PropertyValueFactory
.
最好使用 lambda(或用于非常复杂的单元值工厂的自定义类)来完成此操作。使用 lambda 单元工厂具有类型安全和编译时检查的优点PropertyValueFactory
没有(有关更多信息,请参阅先前引用的答案)。
定义 lambda 而不是 PropertyValueFactories 的示例
SimpleStringProperty
记录的 lambda 单元工厂定义的示例用法String
field:
TableColumn<Person, String> lastColumn = new TableColumn<>("Last");
lastColumn.setCellValueFactory(
p -> new SimpleStringProperty(p.getValue().last())
);
我们可以明确其类型p
变量,为了清楚起见:对 a 的引用TableColumn.CellDataFeatures https://openjfx.io/javadoc/20/javafx.controls/javafx/scene/control/TableColumn.CellDataFeatures.html object.
lastColumn.setCellValueFactory(
( TableColumn.CellDataFeatures < Person, String > p ) -> new SimpleStringProperty (p.getValue().last())
);
有必要将记录字段包装在属性或绑定中,因为单元值工厂实现需要可观察值作为输入。
ReadOnlyStringWrapper
您也许可以使用ReadOnlyStringWrapper https://openjfx.io/javadoc/20/javafx.base/javafx/beans/property/ReadOnlyStringWrapper.html代替SimpleStringProperty https://openjfx.io/javadoc/20/javafx.base/javafx/beans/property/SimpleStringProperty.html, 像这样:
lastColumn.setCellValueFactory(
p -> new ReadOnlyStringWrapper(p.getValue().last()).getReadOnlyProperty()
);
在一个有效的快速测试中。对于不可变记录,这可能是一种更好的方法,但我还没有彻底测试它以确保安全,因此,为了安全起见,我在本示例的其余部分中使用了简单的读写属性。
对于其他类型的数据
同样,对于一个int
field:
TableColumn<Person, Integer> ageColumn = new TableColumn<>("Age");
ageColumn.setCellValueFactory(
p -> new SimpleIntegerProperty(p.getValue().age()).asObject()
);
需要放的asObject()
lambda 末尾的内容在这里进行了解释,以防您好奇(但这只是 JavaFX 框架使用 java 泛型的一个奇怪的方面,不值得花费大量时间研究,只需添加asObject()
在 IMO 上呼叫并移动):
- 为什么 asObject 允许将 SimpleLongProperty 与 setCellValueProperty 一起使用? https://stackoverflow.com/questions/27464221/why-does-asobject-allow-use-of-simplelongproperty-with-setcellvalueproperty
同样,如果您的记录包含其他对象(或其他记录),那么您可以定义一个单元格值工厂SimpleObjectProperty<MyType>
.
Note:这种 lambda 单元工厂定义方法和上面定义的模式也适用于标准(非记录)类。这里没有什么特别的记录。唯一需要注意的是在访问器名称之后注意使用正确的访问器名称getValue()
调用 lambda。例如,使用first()
而不是标准getFirst()
您通常会在类上定义该调用以支持标准 Java Bean 命名模式。这样做的真正好处是,如果您错误地定义了访问器名称,您将收到编译器错误,并且在尝试运行代码之前就知道确切的问题和位置。
示例代码
基于问题中的代码的完整可执行示例。
人.java
public record Person(String last, String first, int age) {}
RecordTableViewer.java
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class RecordTableViewer extends Application {
@Override
public void start(Stage stage) {
TableView<Person> table = new TableView<>();
TableColumn<Person, String> lastColumn = new TableColumn<>("Last");
lastColumn.setCellValueFactory(
p -> new SimpleStringProperty(p.getValue().last())
);
TableColumn<Person, String> firstColumn = new TableColumn<>("First");
firstColumn.setCellValueFactory(
p -> new SimpleStringProperty(p.getValue().first())
);
TableColumn<Person, Integer> ageColumn = new TableColumn<>("Age");
ageColumn.setCellValueFactory(
p -> new SimpleIntegerProperty(p.getValue().age()).asObject()
);
//noinspection unchecked
table.getColumns().addAll(lastColumn, firstColumn, ageColumn);
table.getItems().addAll(
new Person("Smith", "Justin", 41),
new Person("Smith", "Sheila", 42),
new Person("Morrison", "Paul", 58),
new Person("Tyx", "Kylee", 40),
new Person("Lincoln", "Abraham", 200)
);
stage.setScene(new Scene(table, 200, 200));
stage.show();
}
}
Should PropertyValueFactory
被“固定”记录?
记录字段访问器遵循自己的访问命名约定,fieldname()
,就像 Java Bean 一样,getFieldname()
.
可能会提出增强请求PropertyValueFactory
更改其在核心框架中的实现,以便它也可以识别记录访问器命名标准。
不过我不相信更新PropertyValueFactory
识别记录字段访问器将是一个好主意。
更好的解决办法是不更新PropertyValueFactory
用于记录支持并仅允许本答案中概述的类型安全自定义单元格值方法。
我相信这一点是因为 kleopatra 在评论中提供的解释:
自定义 valueFactory 绝对是可行的方法:) 即使实现与 PropertyValueFactory 等效的东西对某些人来说可能很有吸引力 - 但是:这将是一个坏主意,看看“数据未显示在表中”类型的问题的绝对数量“由于打字错误..