如何将 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 记录一起使用? 的相关文章

  • ListChangeListener wasPermutated 块

    ListChangeListener 的 JavaDoc 提供了用于处理更改的模板 但是 我不知道如何处理排列 对于每个索引 我都可以找到该项目的新索引在哪里 但我不知道如何处理它 这是一个独立于编程语言的难题 ObservableList
  • 我们可以像 swing JSplitPane 一样在 Javafx SplitPane 上添加 OneTouchExpansable 按钮吗

    我可以在 JavaFX 上使用 css java api 库添加 OneTouchExpandable 按钮吗SplitPane Swing 中已经存在此选项JSplitPane 但我在 JavaFX API 上找不到该选项 我想 我们可以
  • 如何加载图像文件到ImageView?

    我试图在从文件选择器中选择图像文件后立即显示该图像文件 文件选择器仅限于 png 和 jpg 文件 所选文件存储在文件类型的变量中 为此 我设置了一个 ImageView 我希望用这个新文件设置图像 唯一的问题是它的类型是文件而不是图像 如
  • Java NoSuchMethodException - 类中确实存在方法

    我正在构建一个 JavaFx 应用程序 我想创建一个接收 GridPane 和 Node 以及添加到窗格中的项目数量的方法 但是 当我调用该方法时 我收到 NoSuchMethodException 作为测试 我尝试创建一个简单的方法pri
  • 如何从具有重复条目的过滤列表中删除特定索引?

    我有一个TableView由一个支持SortedList包裹一个FilteredList包裹一个ObservableList 过滤列表中的项目可以重复 也就是说 有可能是这样的情况list get 5 list get 10 用户可以选择行
  • JavaFX颜色选择器的语言

    有没有办法改变语言ColorPicker的文本 例如 自定义颜色 当前颜色 新颜色 色相 饱和度 亮度 不透明度 保存 使用 取消 编辑 以下答案适合那些需要更多内容的人exotic语言 如果您使用其中之一 de es fr it ja k
  • Gluon 移动 iOS 音频播放器

    由于 JavaFx Media 尚未移植到移动平台 任何人都可以帮助我使用本机 iOS APi 来播放声音 mp3 文件 该文件将存储在我的 gluon 项目的 main resources 文件夹中 在 Android 上 我们可以轻松地
  • 如何在谓词中对 FilteredList 结果进行优先级排序/排名?

    我的应用程序包含一个TextField and a ListView The TextField允许用户输入搜索词来过滤内容ListView当他们打字时 过滤过程将匹配每个字段中的多个字段DataItem in the ListView如果
  • JavaFX 打印自定义纸张尺寸

    在 JavaFX 中 我想将照片打印到 10x15 的纸张上 有一些纸张常数 但没有 100x150 mm 常数 是否可以创建自己的纸张以在页面布局中使用它 Thanks PageLayout pageLayout printer crea
  • 在JavaFX中如何在表视图中添加带有数据的组合框

    我已经尝试了很多 但无法将数据库中的所有值填充到我的组合框表格单元格中 控制器 java public class controller GetConnection gc new GetConnection PreparedStatemen
  • JavaFX 2 XYChart.Series 和 setOnMouseEntered

    是否可以设置 XYChart Series 的实例来作用于 setOnMouseEntered 在我看来 使其工作的一个前提条件是实现 EventTarget 接口 至于JavaFX XYChart Series 当光标触摸黄线时 我想突出
  • 仅当用户开始输入时清除 JavaFX TextField 中的提示文本

    默认行为是当字段获得焦点时 字段中的提示文本将被删除 那是标记在场上的时候 是否可以配置文本字段 以便仅在用户开始输入时删除提示文本 否则 我需要在每个文本字段旁边 上方添加一个标签 以描述其中的值 我知道它有点旧 但我自己也需要它 这仍然
  • 更改在不同场景中输入的新场景中的标签文本(javafx)

    我正在尝试更改标签中的文本 该文本是在不同场景的文本字段中输入的文本 我制作了 2 个 FXML 文件 第一个包含一个文本字段和 确定 按钮 第二个包含一个标签 带有文本 标签 我的目标是在文本字段中输入文本 当我按 确定 gt 打开新场景
  • 如何在JavaFX中为TextArea设置圆角?

    我需要在 TextArea 上有圆角 但它看起来有点奇怪 看起来 有些内层也应该有相同半径的圆角 但是哪一个呢 我使用这个CSS text area fx background color dbb1b1 fff0f0 fx backgrou
  • JavaFX:setWrapText(true) (WordWrap) 在 ListView 中不起作用

    在 ListView Cell 中激活 WordWrap 时 文本不会换行 这是一个例子 public class ListBug extends Application public static void main String arg
  • Mac 上的 JavaFX WebView 字体问题

    有些网站显示乱码而不是正确的文本 它只发生在 Mac 上 For example with GMapsFX 可能与 OS X 10 11 或 10 12 有关 我用Java 1 8 0 121测试了它 此问题有任何修复或解决方法吗 就我而言
  • JavaFX - 当文本字段具有焦点时加速器不工作

    在我的应用程序中 我有一个使用加速器的屏幕 我正在使用功能键 F3 在我的应用程序中执行操作 它每次都工作正常 但是当我单击此屏幕上的任何文本字段时 功能键不会执行 这是我设置加速器的代码 scene getAccelerators put
  • 如何在 JavaFX 中对单选按钮的最大可选复选框进行限制?

    请看下面的图片 您就会了解我的应用程序的布局 我希望能够动态选择多少CheckBox 启用下拉菜单 是可选的 固定数量 我想用这 3 个人来实现这个目标RadioButton 在垂直模式下全部 4CheckBox必须选择 不少于 在混合模式
  • 我在使用 JavaFX 绘制十字时遇到问题

    我正在尝试编写代码 在网格上对角绘制 3 个形状 前两个形状是正方形和圆形 我能做到 然而 第三种形状让我有些悲伤 我应该画一个十字 T 版本 而不是 X 每次我写出代码时 它看起来就像一个侧面 我知道我只是错过了一些简单的东西 但我真的很
  • javafx android 中的文本字段和组合框问题

    我在简单的 javafx android 应用程序中遇到问题 问题是我使用 gradle javafxmobile plugin 在 netbeans ide 中构建了非常简单的应用程序 其中包含一些文本字段和组合框 我在 android

随机推荐