У меня есть ObservableList пользовательских объектов RecipeObject_Fermentable, свойства которых я показываю пользователю через TableView. По большей части это работает хорошо; когда я заполняю ObservableList новым элементом, TableView отображает его содержимое.

Использование метода .setCellValueFactory() для каждого столбца позволяет мне очень легко отображать простые объекты (строки и двойники) в виде текста в моем TableView. Например, я могу получить доступ к свойству 'name' типа String с помощью ...

private TableColumn<RecipeObject_Fermentable, String> tableColumn_rf_name;
tableColumn_rf_name.setCellValueFactory(new PropertyValueFactory<>("name"));

Проблема в том, что я хочу показать свойство 'weight' (типа Double) внутри Spinner для столбца tableColumn_rf_weight (и иметь он отображает единицы измерения и его можно редактировать по тексту, но это не главная проблема), и я не могу понять, как я могу это сделать и что лучше всего.

Я пытался найти решение через сообщения других людей, но не могу заставить его работать. Я частично объясняю это тем, что не понимаю, что на самом деле представляют собой методы .setCellFactory() и .setCellValueFactory() для каждого TableColumn и как они связаны со свойствами настраиваемых объектов в моем ObservableList. Если бы кто-то мог вкратце объяснить это или указать на что-то, что имеет значение, я был бы признателен. [РЕДАКТИРОВАТЬ: Я ДЕЙСТВИТЕЛЬНО попытался изучить это, но я не нашел полного объяснения, которое я мог бы понять из моего текущего понимания Java и JavaFX]

Итак, как я могу заставить TableView отображать счетчик для каждой записи данных этого столбца?

Следующие ниже фрагменты кода сокращены, чтобы отображать только полезную информацию. Сообщите мне, если я что-нибудь пропустил.

Пользовательский объект

public class RecipeObject_Fermentable {

// Object properties
private String name;            // Displayed and referenced name of the fermentable
private Double srm;             // Colour of fermentable in SRM
private Double pkgl;            // Specific gravity points per kg per liter
private Double weight;          // Weight in kilograms
private Double percent;         // Total percent to grain bill as percentage
private BooleanProperty lateadd;// Late addition toggle

// Constructor
public RecipeObject_Fermentable(String name, Double weight, Double srm, Double pkgl, Double contribution, boolean lateadd) {
    this.name = name;
    this.srm = srm;
    this.pkgl = pkgl;
    this.weight = weight;
    this.percent = contribution;
    this.lateadd = new SimpleBooleanProperty(lateadd);
}

// Constructor from fermentable object
public RecipeObject_Fermentable(Fermentable f) {
    this.name = f.getName();
    this.srm = f.getSrm();
    this.pkgl = f.getPkgl();
    if (f.getType().equals(FermType.GRAIN)) {
        if (f.getSubtype().equals(FermSubtype.BASE_MALT)) {
            this.weight = Double.valueOf(5);
        } else {
            this.weight = Double.valueOf(1);
        }
    } else {
        this.weight = Double.valueOf(0);
    }
    this.percent = Double.valueOf(0);
    this.lateadd = new SimpleBooleanProperty(false);
}

public String getName() {
    return this.name;
}

public Double getSrm() {
    return this.srm;
}

public Double getPkgl() {
    return this.pkgl;
}

public Double getWeight() {
    return this.weight;
}

public Double getContribution() {
    return this.percent;
}

public ObservableBooleanValue isLateadd() {
    return lateadd;
}

public void setName(String name) {
    this.name = name;
}

public void setSrm(Double srm) {
    this.srm = srm;
}

public void setPkgl(Double pkgl) {
    this.pkgl = pkgl;
}

public void setWeight(Double value) {
    this.weight = value;
}

public void setContribution(Double contribution) {
    this.percent = contribution;
}

public void setLateadd(Boolean checked) {
    this.lateadd.set(checked);
}
}

Другой код в моем контроллере

// Link and define FXML objects for tableView and its columns
@FXML
private TableView<RecipeObject_Fermentable> tableview_recipeFermentables;
@FXML
private TableColumn<RecipeObject_Fermentable, String> tableColumn_rf_name;
@FXML
private TableColumn<RecipeObject_Fermentable, Double> tableColumn_rf_weight;
@FXML
private TableColumn<RecipeObject_Fermentable, Double> tableColumn_rf_percent;
@FXML
private TableColumn<RecipeObject_Fermentable, Boolean> tableColumn_rf_lateAddition;

// Set up observable list of custom object
private ObservableList<RecipeObject_Fermentable> fermentables_recipe = 
FXCollections.observableArrayList()

// Set up table data
tableview_recipeFermentables.setItems(fermentables_recipe);
tableColumn_rf_name.setCellValueFactory(new PropertyValueFactory<>("name"));
// ---> tableColumn_rf_weight.setCellValueFactory();
// ---> tableColumn_rf_weight.setCellFactory();
tableColumn_rf_percent.setCellValueFactory(new PropertyValueFactory<>("percent"));
tableColumn_rf_lateAddition.setCellValueFactory(p->p.getValue().isLateadd());
tableColumn_rf_lateAddition.setCellFactory(CheckBoxTableCell.forTableColumn( tableColumn_rf_lateAddition));

Заранее спасибо.

0
Joe 23 Апр 2018 в 12:27

1 ответ

Лучший ответ

Вам нужен собственный TableCell. Кроме того, должен быть способ передать данные обратно элементу. Лучше всего это сделать с помощью DoubleProperty, но в вашем случае вы можете работать с JavaBeanDoubleProperty, если значения null не разрешены).

final JavaBeanDoublePropertyBuilder weightBuilder
                   = JavaBeanDoublePropertyBuilder.create()
                                                  .beanClass(RecipeObject_Fermentable.class)
                                                  .name("weight");
tableColumn_rf_weight.setCellValueFactory(cd -> weightBuilder.bean(cd.getValue()).build());
public class DoubleSpinnerCell<T> extends TableCell<T, Number> {
    private final Spinner<Double> spinner = new Spinner​(0, Double.MAX_VALUE, 0, 0.01);
    private boolean ignoreUpdate; // flag preventing updates triggered from ui/initialisation

    {
        spinner.valueProperty().addListener((o, oldValue, newValue) -> {
            if (!ignoreUpdate) {
                ignoreUpdate = true;
                WritableValue<Number> property = (WritableValue<Number>) getTableColumn().getCellObservableValue((T) getTableRow().getItem());
                property.setValue(newValue);
                ignoreUpdate = false;
            }
        });
    }


    @Override
    protected void updateItem(Number item, boolean empty) {
        super.updateItem(item, empty);

        if (empty || item == null) {
            setGraphic(null);
        } else {
            ignoreUpdate = true;
            spinner.getValueFactory().setValue(item.doubleValue());
            setGraphic(spinner);
            ignoreUpdate = false;
        }
    }
}
tableColumn_rf_weight.setCellFactory(c -> new DoubleSpinnerCell<RecipeObject_Fermentable>());

Однако для этого требуется, чтобы weight был установлен в ненулевое значение. (Я рекомендую использовать примитив double вместо Double.)

Редактировать

Вам также необходимо изменить тип элемента столбца на Number:

@FXML
private TableColumn<RecipeObject_Fermentable, Number> tableColumn_rf_weight;
0
Community 20 Июн 2020 в 09:12