使用嵌套对象的 JavaFX 嵌套列

JavaFX nested columns using nested object

我有以下数据模型

public interface Foo {
  public DoubleProperty getA();
  public DoubleProperty getB();
}

public interface Bar {
  public Foo getAttribute1();
  public Foo getAttribute2();
}

我想使用 TableView 显示 Bar 列表,其中 A 和 B 是相关属性的嵌套列,例如

| Attribute1 | Attribute2 |
|   A  |  B  |   A  |  B  | 
|------------|------------|

我可以在设置单个单元格值工厂的地方使它正常工作,但是有很多重复代码,其中唯一的变化是 getAttribute1() 与 getAttribute2()。该模型出现的频率很高,因此消除 重复的代码将非常有用。

所以我开始组合一个 class 来创建必要的嵌套列,大致如下:

public class FooColumn<S> {
  private TableColumn<S,Double> aColumn;
  private TableColumn<S,Double> bColumn;

  public FooColumn(TableColumn parent) {
    aColumn = new TableColumn<>("A");
    bColumn = new TableColumn<>("B");

    parent.getColumns().addAll(aColumn,bColumn);
  }
}

挑战在于为嵌套列设置单元格值工厂。我不想使用反射来进行绑定。想到的一种解决方案是使 FooColumn 抽象并为 Attribute1 和 Attribute2 制作具体的 classes 以定义适当的单元格值工厂。我发现该解决方案比重复代码方法更好,但不太理想。另一种解决方案是在 returns 适当属性的父列中定义一个单元格值工厂,而不显示该值。嵌套列然后使用父列单元格值方法获取 Foo 对象。第三种解决方案是将 Callback 作为访问 Bar 中适当属性的构造函数的一部分传递。

第三种解决方案似乎是最干净的方法,但我觉得我缺少更优雅的解决方案,非常感谢任何建议。

您可以使用这样的创建方法:

public record NamedFunction<S,T>(String title, Function<S,T>> property) ;

public TableColumn<Bar, Foo> createParentColumn(String title, Function<Bar, Foo> fooProvider, List<NamedFunction<Foo, DoubleProperty> properties) {
    TableColumn<Bar, Foo> col = new TableColumn<>(title);
    for (NamedFunction func : properties) {
        TableColumn<Bar, Double> childCol = new TableColumn<>(func.title());
        childCol.setCellValueFactory(cd -> properties.property().apply(fooProvider.apply(cd.getValue())));
        col.getColumns().add(childCol);
    }
    return col ;
}

然后,以你的例子为例:

List<NamedFunction<Foo, DoubleProperty>> barProperties = List.of(
    new NamedFunction("A", Foo::getA),
    new NamedFunction("B", Foo::getB)
);
List<NamedFunction<Bar, Foo>> fooProperties = List.of(
    new NamedFunction(Bar::getAttribute1),
    new NamedFunction(Bar::getAttribute2)
);

TableView<Bar> table = new TableView<>();
for NamedFunction<Bar, Foo> f : fooProperties) {
    table.getColumns().add(createParentColumn(f.title(), f.property(), barProperties);
}

在按名称显式执行方法时使用反射是在第一种情况下减少代码与第二种情况下的简单性和性能之间的权衡。

这种方法的好处是您可以隔离在这些之间进行选择的机会。在上面的示例中,这些与列表的创建是隔离的。