如何为JavaFX TableView列制作通用TableColumn渲染器

时间:2019-01-16 09:01:59

标签: javafx tableview tablecolumn

在javaFX视图控制器的initialize方法中,我有一个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(..) { }

在列中占据表单元格的变量是在这样的对象定义中定义的:

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

渲染列:因此该方法至少需要表列和variable属性:

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

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

1 个答案:

答案 0 :(得分:2)

“传递方法”的唯一方法是创建一个包含执行此操作的逻辑的对象,或者使用方法引用。

此外,即使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的操作要容易得多。)否则,您可以简单地硬编码第二个类型参数和其他功能,而只保留第一个类型方法的两个参数。

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