【问题标题】:JavaFX nested columns using nested object使用嵌套对象的 JavaFX 嵌套列
【发布时间】:2021-05-27 04:04:55
【问题描述】:

我有以下数据模型

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()。这种模式经常出现,所以消除 重复的代码会很有用。

所以我开始整理一个创建必要的嵌套列的类,类似于:

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 创建具体类,以定义适当的单元格值工厂。我发现该解决方案比重复代码方法更好,但不太理想。另一种解决方案是在父列中定义一个单元格值工厂,它返回适当的属性而不显示值。然后嵌套列使用父列单元格值方法来获取 Foo 对象。第三种解决方案是将 Callback 作为访问 Bar 中适当属性的构造函数的一部分传递。

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

【问题讨论】:

  • 不清楚是什么问题。你这样做,例如column1a.setCellValueFactory(cd -&gt; cd.getValue().getAttribute1.getA());
  • 在 FooColumn 类中不知道正在访问哪个属性。因此,我需要为 FooColumn 之外的每个嵌套列设置单元格值工厂。
  • 您的解决方案是包含大量重复代码的原始实现。它有效,但它是一个复制面食节日
  • 这是正常的做法。不清楚为什么要继承 TableColumn... 只需编写一个带有适当参数的方法来创建列。 (更喜欢创建模式而不是继承。)
  • 我没有继承 TableColumn,但你说得对,创建模式是一种好方法。看来您的解决方案是第三种解决方案的变体。

标签: java javafx nested-object


【解决方案1】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 2012-09-21
    • 2017-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 2014-05-06
    • 2021-12-23
    • 2018-10-12
    相关资源
    最近更新 更多