在 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"));
问题是,我想在 Spinner
列 Spinner
中显示 '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;
我有 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"));
问题是,我想在 Spinner
列 Spinner
中显示 '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;