如何为 JavaFX TableView 列创建一个通用的 TableColumn 渲染器

How do I make a generic TableColumn renderer for JavaFX TableView column

在 javaFX 视图控制器的初始化方法中,我有一个显示财务金额的 TableView 列渲染器,每个单元格都根据金额呈现为红色或蓝色,颜色为红色或蓝色借记或贷记金额。

@Override
  public void initialize(URL url, ResourceBundle rb) 
 budgetAmountCol.setCellValueFactory(cellData ->       cellData.getValue().budgetAmountProperty());
// Custom rendering of the budget amount cells in the column.
 budgetAmountCol.setCellFactory((TableColumn<TxnObject, String> column) -> {
return new TableCell<TxnObject, String>() {
    @Override
    protected void updateItem(String item, boolean empty) {
      super.updateItem(item, empty); 
      if (item == null || empty) {
      }
      else {
        double amt = Double.parseDouble(item);
        if (amt == 0) {
          setId("zero-amt");
        } else {
          if (amt < 0){
            item = item.substring(1, item.length());
            setId("debit-amt"); 
          } else {
            setId("credit-amt");               
          }
        }
      }
      setText(item);
    }
  };
});

因为我有几个列需要类似地呈现,所以我试图避免大量的源代码并将上面的代码变成一个可以传递参数的方法,从而呈现列,比如

private void renderColumn(..) { }

占用列中table单元格的变量在对象定义中定义如下:

public class TxnObject {
      ..
      private final StringProperty budgetAmount;

    ..
    public String getbudgetAmount() {
        double d = Double.parseDouble(budgetAmount.get());
        setbudgetAmount(MainApp.formatAmount(d));
        return budgetAmount.get();
      }

      public void setbudgetAmount(String budgetAmount) {
        this.budgetAmount.set(budgetAmount);
      }

      public StringProperty budgetAmountProperty() {
        return budgetAmount;
      }

因此需求是传递代码第二行的内容,即

budgetAmountCol.setCellValueFactory(cellData -> cellData.getValue().budgetAmountProperty());

渲染列:因此该方法至少需要 table 列和变量 属性:

private void renderColumn(TableColumn<TxnObject, String> tcol, StringProperty sprop) {
    node.setCellValueFactory(..)
}

但是我无法正确调用该方法。我已经尝试了以下结果:

renderColumn(budgetAmountCol, budgetAmountProperty());
syntax error: no method BudgetAmountProperty()
renderColumn(budgetAmountCol, cellData.getValue().budgetAmountProperty());
syntax error: Cannot find symbol CellData
renderColumn(budgetAmountCol, cellData ->    cellData.getValue().budgetAmountProperty());
syntax error: StringProperty is not a functional interface

我发现语法和理解如何实现我的目标相当具有挑战性,如果我能得到一些建议来尝试实现解决方案,我将不胜感激。

"pass a method" 的唯一方法是创建一个包含执行此操作的逻辑的对象或使用方法引用。

此外,即使 JavaFX 不强制执行这些限制,id 也应该是唯一的。请改用伪类。

你可以使用

private static final PseudoClass ZERO = PseudoClass.getPseudoClass("zero-atm");
private static final PseudoClass CREDIT = PseudoClass.getPseudoClass("credit-atm");
private static final PseudoClass DEBIT = PseudoClass.getPseudoClass("debit-atm");

public static <S, T> void renderColumn(TableColumn<S, T> column,
        final Callback<? super S, ObservableValue<T>> extractor, final Callback<? super T, String> converter,
        final Comparator<T> comparator, final T zero) {
    if (extractor == null || comparator == null || zero == null) {
        throw new IllegalArgumentException();
    }

    column.setCellValueFactory(cd -> extractor.call(cd.getValue()));
    column.setCellFactory(col -> new TableCell<S, T>() {
        @Override
        protected void updateItem(T item, boolean empty) {
            super.updateItem(item, empty);

            pseudoClassStateChanged(ZERO, false);
            pseudoClassStateChanged(CREDIT, false);
            pseudoClassStateChanged(DEBIT, false);

            if (empty || item == null) {
                setText("");
            } else {
                setText(converter.call(item));
                int comparison = comparator.compare(zero, item);
                if (comparison == 0) {
                    pseudoClassStateChanged(ZERO, true);
                } else {
                    pseudoClassStateChanged(comparison < 0 ? CREDIT : DEBIT, true);
                }
            }
        }
    });
}

public static <S, T extends Comparable<T>> void renderColumn(TableColumn<S, T> column,
        Callback<? super S, ObservableValue<T>> extractor, Callback<? super T, String> converter, T zero) {
    renderColumn(column, extractor, converter, Comparator.naturalOrder(), zero);
}

假设您将 budget 的类型更改为 ObjectProperty<BigDecimal>,可以调用该方法,如下所示。 (BigDecimal 除了避免舍入错误外,在比较值和进行其他数学运算时更容易使用。)否则,您可以简单地对第二个类型参数和其他功能进行硬编码,而只保留第一个方法的2个参数。

renderColumn(column, TxnObject::budgetAmountProperty, (BigDecimal val) -> val.abs().toString(), BigDecimal.ZERO);