如何将 JavaFX TableView 与 java 记录一起使用?

2024-04-11

Records是一个新功能Java 16 https://en.wikipedia.org/wiki/Java_version_history#Java_16。定义于JEP 395:记录 https://openjdk.org/jeps/395.

假设您有这样的记录。

public record Person(String last, String first, int age)
{
    public Person()
    {
        this("", "", 0);
    }
}

这是最后一堂课。它自动生成 getter 方法first()、last() 和age()。

现在这是 JavaFX 中的 TableView。

/**************************************************
*   Author: Morrison
*   Date:  10 Nov 202021
**************************************************/

import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.control.TableView;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.PropertyValueFactory;

public class TV extends Application
{
    public TV()
    {
    }

    @Override
    public void init()
    {
    }

    @Override
    public void start(Stage primary)
    {
        BorderPane root = new BorderPane();
        TableView<Person> table = new TableView<>();
        root.setCenter(table);

        TableColumn<Person, String> lastColumn = new TableColumn<>("Last");
        lastColumn.setCellValueFactory(new PropertyValueFactory<Person,
            String>("last"));

        TableColumn<Person, String> firstColumn = new TableColumn<>("First");
        firstColumn.setCellValueFactory(new PropertyValueFactory<Person,
            String>("first"));

        TableColumn<Person, Integer> ageColumn = new TableColumn<>("Age");
        ageColumn.setCellValueFactory(new PropertyValueFactory<Person,
            Integer>("age"));

        table.getColumns().add(lastColumn);
        table.getColumns().add(firstColumn);
        table.getColumns().add(ageColumn);
        table.getItems().add(new Person("Smith", "Justin", 41));
        table.getItems().add(new Person("Smith", "Sheila", 42));
        table.getItems().add(new Person("Morrison", "Paul", 58));
        table.getItems().add(new Person("Tyx", "Kylee", 40));
        table.getItems().add(new Person("Lincoln", "Abraham", 200));
        Scene s = new Scene(root, 500, 500);
        primary.setTitle("TableView Demo");
        primary.setScene(s);
        primary.show();
    }

    @Override
    public void stop()
    {
    }
}

问题就出在这里。

TableColumn<Person, String> lastColumn = new TableColumn<>("Last");
        lastColumn.setCellValueFactory(new PropertyValueFactory<Person,
            String>("last"));
        

TableView 需要 name 的方法getFirst, getLast, and getAge做它的工作。除了做这种可怕的事情之外,还有其他解决方法吗?

public record Person(String last, String first, int age)
{
    public Person()
    {
        this("", "", 0);
    }
    public String getLast()
    {
        return last;
    }
    public String getFirst()
    {
        return first;
    }
    public int getAge()
    {
        return age;
    }
}

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 等效的东西对某些人来说可能很有吸引力 - 但是:这将是一个坏主意,看看“数据未显示在表中”类型的问题的绝对数量“由于打字错误..

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将 JavaFX TableView 与 java 记录一起使用? 的相关文章

  • 如何告诉 JavaFX WebView 忽略“use strict”指令?

    我正在尝试使用以下代码将 mozilla 查看器集成到 JavaFx WebView 中 import javafx application Application import javafx scene Scene import java
  • 如何避免每次创建新的 javafx 项目时添加 vm 参数?

    我正在使用 e fx clipse 插件和 scenebuilder 在 STS 4 6 1 中创建 javafx 项目 每次创建新项目时 我都必须在运行配置中添加虚拟机参数 module path home path to javafx
  • 由于 MultiReleaseException,jdeps 无法打印模块 deps

    我们有一个基于 JavaFX 的应用程序 它是not模块化 有原因 涉及遗留库 但我们使用构建自定义运行时jdeps and jlink 我们最近重写了该应用程序并添加了一些新的依赖项 并删除了其他依赖项 现在 正在构建应用程序的脚本突然停
  • JavaFX 8:拦截应用程序“退出”

    为了验证用户所做的所有更改是否已保存 我想拦截 JavaFX 应用程序的退出 退出 是否有一种通用的方法来实现这一目标 例如覆盖事件 或者还有更多方法吗 正如他们已经说过的 这是通过拦截来完成的WindowEvent WINDOW CLOS
  • 为什么图很大时x轴消失了

    我正在尝试使用加载大图JFreeChart 但是 当缓冲图像超过一定大小时 X 轴会出现问题 这些值在 X 轴上消失 这可以在图像的第三张图中看到 I would appreciate any help in fixing the prob
  • 如何在 Javafx 中对齐对话框窗格的“确定”按钮?

    我想对齐 即位置中心 对话框窗格的 确定 按钮 我已经尝试过下面的代码 但它不起作用 Dialog dialog new Dialog DialogPane dialogPane dialog getDialogPane dialogPan
  • 可以将矩形设置为显示边框吗?

    以下应用 public class Temp extends Application Override public void start Stage primaryStage StackPane root new StackPane Re
  • JavaFX 中按下按钮的样式

    我有一个Button in my FXML文件 我在下面给它一个样式CSS button fx background color linear gradient ff5400 be1d00 fx background radius 30 f
  • 添加样式后如何重置回默认CSS?

    基本上 我通过添加如下样式类来更改 javafx 中文本字段的 css textfield getStyleClass add textfieldstyle 但后来我希望能够将其恢复到原来的样子 但由于本例中的原始外观是 JavaFX 的默
  • 如何根据鼠标位置显示工具提示? - JavaFX

    我有一个stackPane 充满一个圆圈和几条线 我想在将鼠标悬停在 StackPane 上时显示工具提示 并且工具提示应包含X Y coords鼠标的 我知道如何获取鼠标的坐标 但我无法找到显示工具提示的方法 你们中有人能帮我吗 安舒尔
  • JavaFX 使用动画最小化和最大化未装饰的舞台

    我在这个问题中使用已接受的答案 JavaFX 最小化未修饰的阶段 https stackoverflow com questions 26972683 javafx minimizing undecorated stage正确最小化我的应用
  • 将两个表视图绑定在一起,以便它们同步滚动

    我想将两个表视图绑定在一起 以便它们同步滚动 我怎么做 我无法找到如何访问表格视图的滚动条 我做了一个CSS hack来将Tableview与外部滚动条绑定 一个滚动条控制两个表格视图 我的想法的概述 创建两个表视图 制作一个垂直滚动条 在
  • 从后台线程更新可观察列表的正确方法

    我正在尝试遵循 MVC 进行测试项目 因此我的模型应该完全独立于我的观点 但是我不确定应该如何更新在后台线程中更新的可观察列表 正在给出有关上传的字符串 文件 以便消息显示在 UI 上的 ListView 中 我正在使用 JavaFX 并尝
  • 如何从我自己的线程安全地修改 JavaFX GUI 节点?

    我尝试更改线程中的 JavaFX GUI 节点 但看到以下错误 线程 Thread 8 中的异常 java lang IllegalStateException 不存在 FX应用线程 当前线程 线程 8 生成错误的示例代码 public c
  • 尝试让 GUI 使用 arrayList 在牌组中打印随机卡

    所以我目前正在用java开发一个卡牌战争游戏 我试图让 GUI 屏幕使用 arrayList 从一组卡片图像中打印 2 张随机卡片 必须使用它进行分配 卡片图像文件名为 1 png 2 png 52 png 并存储在 image card
  • JavaFX TabPane - 每个选项卡一个控制器

    我是 Fx 新手 我有一个带有 10 个选项卡的 TabPanel 每个选项卡都有很多控件 图表 按钮等 我想要的是为每个选项卡分配一个控制器 SceneBuilder 只让我为整个视图分配一个控制器 我的意思是 只有顶部面板 根 具有 控
  • 如何使用 JavaFX 中的 JCSG 库将 MeshView 转换为 CSG 对象

    我正在使用 JavaFX 的 JCSG 库 我有一些MeshView我想将它们转换成的对象CSG对象 有办法实现吗 最简单的方法是组合javafx scene shape Mesh对象与 CSG 对象 前提是您有TriangleMesh正在
  • 如何使 AnchorPane 大小响应最大化的屏幕视图

    我不知道什么是灰色地带以及如何扩展我的Anchorpane到整个屏幕Image https i stack imgur com m5HqA png 不是未最大化的视图是这样的image2 https i stack imgur com 14
  • JavaFX TextField cancelEdit 未按预期工作

    I have a TextField https docs oracle com javase 8 javafx api javafx scene control TextField html and I would like the co
  • JavaFX中如何获取鼠标位置?

    我是java fx 的初学者 如何在 JavaFX 中获取鼠标在 x 和 y 中的位置 我尝试使用 AWTMouseInfo 也导入了它 但它不起作用 我还在 Ensembles 中看到了它的代码 在 高级阶段 拖动球窗口 这就是我需要做的

随机推荐