在 TableView 中将 Spinner 实现为自定义对象的 show/edit 值

Implement Spinner in TableView to show/edit value of custom object

我有 ObservableList 个自定义对象 RecipeObject_Fermentable,我通过 TableView 向用户显示其属性。在大多数情况下,它运作良好;当我用新项目填充 ObservableList 时,TableView 会显示其内容。

对每一列使用 .setCellValueFactory() 方法允许我在 TableView 中非常轻松地将简单对象(字符串和双精度)显示为文本。例如,我可以使用...

访问 String 类型的 属性 'name'
private TableColumn<RecipeObject_Fermentable, String> tableColumn_rf_name;
tableColumn_rf_name.setCellValueFactory(new PropertyValueFactory<>("name"));

问题是,我想在 SpinnerSpinner 中显示 'weight' 属性(类型 Double) =24=](并让它显示单位并可以通过文本进行编辑,但这不是主要问题),而且我不知道如何实际做到这一点,什么是最好的练习。

我尝试通过其他人的帖子提出解决方案,但无法实现。我将这部分归因于不了解每个 TableColumn.setCellFactory().setCellValueFactory() 方法实际上是什么和做什么,以及它们如何 link 我的自定义对象的属性可观察列表。如果有人可以简要解释这一点,或指出可以做到的事情,我将不胜感激。 [编辑:我确实尝试对此进行研究,但我没有找到任何我目前对 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));

提前致谢。

您需要定制 TableCell。此外,应该有一种方法可以将数据传回项目。这最好通过使用 DoubleProperty 来完成,但在您的情况下,如果不允许使用 null 值,则可以使用 JavaBeanDoubleProperty

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;