如何将JavaFX TableView与Java记录一起使用? [英] How do you use a JavaFX TableView with java records?

查看:39
本文介绍了如何将JavaFX TableView与Java记录一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Java记录是Java15的新特性。假设您有这样一条记录。

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需要名称为getFirstgetLastgetAge的方法来执行其工作。除了做这件可怕的事情之外,还有其他解决办法吗?

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;
    }
}

推荐答案

解决方案

使用lambda单元格值工厂而不是PropertyValueFactory

有关两者差异的一些解释,请参阅:

为什么这么做

正如您所注意到的,问题在于记录访问器不遵循标准的java bean属性命名约定,而这正是PropertyValueFactory所期望的。例如,记录使用first()而不是getFirst()作为访问器,这使得它与PropertyValueFactory不兼容。

您是否应该应用一种解决办法&执行向记录添加附加GET方法这件可怕的事情,以便您可以使用PropertyValueFactoryTableView接口?-&&>绝对不是,有一个更好的方法:-)

需要修复的是定义您自己的自定义单元格工厂,而不是使用PropertyValueFactory

这最好使用lambda(或用于真正复杂的单元格值工厂的自定义类)来完成。使用lambda单元工厂具有PropertyValueFactory所没有的类型安全和编译时检查的优势(有关更多信息,请参阅前面引用的答案)。

定义lambdas而不是PropertyValueFaciles的示例

记录String字段的lambda单元工厂定义用法示例:

TableColumn<Person, String> lastColumn = new TableColumn<>("Last");
lastColumn.setCellValueFactory(
        p -> new SimpleStringProperty(p.getValue().last())
);

有必要将记录字段包装在属性或绑定中,因为单元格值工厂实现需要可观察到的值作为输入。

您可以使用ReadOnlyStringWrapper代替SimpleStringProperty,如下所示:

lastColumn.setCellValueFactory(
        p -> new ReadOnlyStringWrapper(p.getValue().last()).getReadOnlyProperty()
);

在一个有效的快速测试中。对于不变的记录,这可能是一种更好的方法,但是为了确保安全,我还没有对其进行彻底的测试,因此,为了安全起见,我在此示例的整个睡觉中使用了简单的读写属性。

同样,对于int字段:

TableColumn<Person, Integer> ageColumn = new TableColumn<>("Age");
ageColumn.setCellValueFactory(
        p -> new SimpleIntegerProperty(p.getValue().age()).asObject()
);

这里解释了将asObject()放在lambda末尾的必要性,以防您感到好奇(但这只是JavaFX框架使用java泛型的一个奇怪方面,不值得花费大量时间进行研究,只需添加asObject()调用并转移到IMO):

同样,如果您的记录包含其他对象(或其他记录),则可以为SimpleObjectProperty<MyType>定义单元格值工厂。

注意:这种用于lambda单元工厂定义和上面定义的模式的方法也适用于标准(非记录)类。这里没有什么特别的记录。唯一需要注意的是注意在lambda中的getValue()调用之后使用正确的访问器名称。例如,使用first()而不是通常在类上定义的标准getFirst()调用来支持标准Java Bean命名模式。其中真正重要的一点是,如果您错误地定义了访问器名称,您将得到一个编译器错误,并且甚至在您尝试运行代码之前就知道确切的问题和位置。

示例代码

基于问题中代码的完整可执行示例。

Person.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();
    }
}

是否应为记录修复PropertyValueFactory(&Q;)?

记录字段访问器遵循其自己的访问命名约定fieldname(),就像Java bean一样,getFieldname()

可能会对PropertyValueFactory提出增强请求,以更改其在核心框架中的实现,以便它还可以识别记录访问器命名标准。

但是,我认为更新PropertyValueFactory以识别记录字段访问器不是一个好主意。

更好的解决方案是不更新PropertyValueFactory以支持记录,而只允许本答案中概述的类型安全自定义单元格值方法。

我相信这一点,因为克利奥帕特拉在评论中提供了这样的解释:

自定义valueFactory绝对是可行的方法:)即使对某些人来说实现等价物PropertyValueFactory看起来很有吸引力-但是:这不是一个好主意,因为表&Quot;中由于打字错误而没有显示大量的&Quot;数据类型的问题。

这篇关于如何将JavaFX TableView与Java记录一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆