Как сделать универсальный рендерер TableColumn для столбца JavaFX TableView - PullRequest
0 голосов
/ 16 января 2019

В контроллере представления 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());

для визуализации столбца: поэтому, как минимум, для метода требуется столбец таблицы и свойство переменной:

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 голосов
/ 16 января 2019

Единственный способ «передать метод» - создать объект, содержащий логику для этого, или использовать ссылку на метод.

Более того, даже если 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);
...