当枚举与自定义单元工厂一起使用时,JavaFX 无法设置数据
JavaFX Unable to set data when enums used with custom cell factory
[附上完整的源代码]
我有一个包含 3 列的 javafx TableView("Name"、"Gender"、"Country")。我将细胞工厂附加到我的所有专栏,如下所示。
nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
genderCol.setCellFactory(ComboBoxTableCell.forTableColumn(GenderEnum.values()));
countryCol.setCellFactory(ComboBoxTableCell.forTableColumn("India", "USA"));
我没有自己实现任何提交逻辑。现在我更改所有 3 列中的值,然后打印数据。我能够在除 genderCol 之外的所有列上看到更改的值。该列的唯一区别是它使用的是枚举。
当我更改 countryCol(也就是 ComoboBoxTableCell)中的值时,countryProperty 首先使用旧值调用两次,然后使用新值调用两次,但对于 genderCol,genderProperty 仅调用一次。
您可以复制并执行代码,先打印数据而不做任何更改,然后更改所有 3 列的值,然后打印数据。现在检查除了 genderCol 其他列有改变的值。
我是否遗漏了枚举或做错了什么
Main.java
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application
{
@Override
public void start(final Stage primaryStage)
{
try
{
final VBox root = FXMLLoader.load(this.getClass().getResource("MainView.fxml"));
final Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
catch(final Exception e)
{
e.printStackTrace();
}
}
public static void main(final String[] args)
{
launch(args);
}
}
MainView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<VBox spacing="10.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<TableView fx:id="table" editable="true">
<columns>
<TableColumn prefWidth="100.0" text="Name" fx:id="nameCol">
<cellValueFactory><PropertyValueFactory property="name" /></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="100.0" text="Gender" fx:id="genderCol">
<cellValueFactory><PropertyValueFactory property="gender" /></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="100.0" text="Country" fx:id="countryCol">
<cellValueFactory><PropertyValueFactory property="country" /></cellValueFactory>
</TableColumn>
</columns>
</TableView>
<Button text="Print Data" onAction="#printData"/>
</children>
</VBox>
MainController.java
package application;
import java.net.URL;
import java.util.ResourceBundle;
import application.Person.GenderEnum;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;
/**
* @author Saravana Kumar M
*
*/
public class MainController implements Initializable
{
@FXML
TableView<Person> table;
@FXML
TableColumn<Person, String> nameCol;
@FXML
TableColumn<Person, GenderEnum> genderCol;
@FXML
TableColumn<Person, String> countryCol;
@Override
public void initialize(final URL location, final ResourceBundle resources)
{
this.nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
this.genderCol.setCellFactory(ComboBoxTableCell.forTableColumn(GenderEnum.values()));
this.countryCol.setCellFactory(ComboBoxTableCell.forTableColumn("India", "USA"));
final Person p1 = new Person("A", "M", "India");
final Person p2 = new Person("B", "F", "USA");
final Person p3 = new Person("C", "N", "India");
final Person p4 = new Person("D", null, "USA");
final Person p5 = new Person("E", "M", "India");
final Person p6 = new Person("F", "F", "USA");
final Person p7 = new Person("G", "N", "India");
final Person p8 = new Person("H", null, "USA");
this.table.setItems(FXCollections.observableArrayList(p1, p2, p3, p4, p5, p6, p7, p8));
this.genderCol.setOnEditCommit((event) -> this.doThis(event));
}
private void doThis(final CellEditEvent<Person, GenderEnum> event)
{
System.out.println(event);
System.out.println(event.getOldValue());
System.out.println(event.getNewValue());
System.out.println(event.getRowValue());
}
@FXML
public void printData()
{
this.table.getItems().forEach(System.out::println);
}
}
Person.java
package application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* @author Saravana Kumar M
*
*/
public class Person
{
private final StringProperty name;
private final ObjectProperty<GenderEnum> gender;
private final StringProperty country;
public Person(final String name, final String gender, final String country)
{
this.name = new SimpleStringProperty(name);
if(gender == null)
{
this.gender = new SimpleObjectProperty<>();
}
else
{
this.gender = new SimpleObjectProperty<>(GenderEnum.valueOf(gender));
}
this.country = new SimpleStringProperty(country);
}
public String getName()
{
return this.name.get();
}
public void setName(final String name)
{
this.name.set(name);
}
public StringProperty nameProperty()
{
return this.name;
}
public GenderEnum getGender()
{
return this.gender.get();
}
public void setGender(final GenderEnum gender)
{
this.gender.set(gender);
}
public ObjectProperty<GenderEnum> genderProperty()
{
return this.gender;
}
public String getCountry()
{
return this.country.get();
}
public void setCountry(final String country)
{
this.country.set(country);
}
public StringProperty countryProperty()
{
return this.country;
}
@Override
public String toString()
{
return "name : " + this.name + " name.get : " + this.name.get() + " gender : " + this.gender + " gender.get : " + this.gender.get() + " country : " + this.country.get() + " country.get : " + this.country.get();
}
public enum GenderEnum
{
M("Male"), F("Female"), N("None");
private final String label;
private GenderEnum(final String label)
{
this.label = label;
}
@Override
public String toString()
{
return this.label;
}
}
}
提前致谢。
By default the TableColumn edit commit handler is non-null, with a
default handler that attempts to overwrite the property value for the
item in the currently-being-edited row. It is able to do this as the
Cell.commitEdit(Object)
method is passed in the new value, and this is
passed along to the edit commit handler via the CellEditEvent
that is
fired. It is simply a matter of calling
TableColumn.CellEditEvent.getNewValue()
to retrieve this value.
It is very important to note that if you call
TableColumn.setOnEditCommit(javafx.event.EventHandler)
with your own
EventHandler
, then you will be removing the default handler. Unless
you then handle the writeback to the property (or the relevant data
source), nothing will happen. You can work around this by using the
TableColumnBase.addEventHandler(javafx.event.EventType, javafx.event.EventHandler)
method to add a
TableColumn.EDIT_COMMIT_EVENT
EventType with your desired EventHandler
as the second argument. Using this method, you will not replace the
default implementation, but you will be notified when an edit commit
has occurred.
既然你有
this.genderCol.setOnEditCommit((event) -> this.doThis(event));
在您的控制器 class 中,您删除了更新 属性 的默认处理程序。如果将其替换为
this.genderCol.addEventHandler(TableColumn.<Person, GenderEnum>editCommitEvent(),
event -> this.doThis(event));
它将按预期工作。 (请注意文档告诉您如何获取事件类型是不正确的。还要注意您的处理程序将在默认处理程序之前被调用,因此 getRowValue()
的输出将显示值 before 更新,但按下按钮显示值将给出新值。)
[附上完整的源代码]
我有一个包含 3 列的 javafx TableView("Name"、"Gender"、"Country")。我将细胞工厂附加到我的所有专栏,如下所示。
nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
genderCol.setCellFactory(ComboBoxTableCell.forTableColumn(GenderEnum.values()));
countryCol.setCellFactory(ComboBoxTableCell.forTableColumn("India", "USA"));
我没有自己实现任何提交逻辑。现在我更改所有 3 列中的值,然后打印数据。我能够在除 genderCol 之外的所有列上看到更改的值。该列的唯一区别是它使用的是枚举。
当我更改 countryCol(也就是 ComoboBoxTableCell)中的值时,countryProperty 首先使用旧值调用两次,然后使用新值调用两次,但对于 genderCol,genderProperty 仅调用一次。
您可以复制并执行代码,先打印数据而不做任何更改,然后更改所有 3 列的值,然后打印数据。现在检查除了 genderCol 其他列有改变的值。
我是否遗漏了枚举或做错了什么
Main.java
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application
{
@Override
public void start(final Stage primaryStage)
{
try
{
final VBox root = FXMLLoader.load(this.getClass().getResource("MainView.fxml"));
final Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
catch(final Exception e)
{
e.printStackTrace();
}
}
public static void main(final String[] args)
{
launch(args);
}
}
MainView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<VBox spacing="10.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<TableView fx:id="table" editable="true">
<columns>
<TableColumn prefWidth="100.0" text="Name" fx:id="nameCol">
<cellValueFactory><PropertyValueFactory property="name" /></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="100.0" text="Gender" fx:id="genderCol">
<cellValueFactory><PropertyValueFactory property="gender" /></cellValueFactory>
</TableColumn>
<TableColumn prefWidth="100.0" text="Country" fx:id="countryCol">
<cellValueFactory><PropertyValueFactory property="country" /></cellValueFactory>
</TableColumn>
</columns>
</TableView>
<Button text="Print Data" onAction="#printData"/>
</children>
</VBox>
MainController.java
package application;
import java.net.URL;
import java.util.ResourceBundle;
import application.Person.GenderEnum;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;
/**
* @author Saravana Kumar M
*
*/
public class MainController implements Initializable
{
@FXML
TableView<Person> table;
@FXML
TableColumn<Person, String> nameCol;
@FXML
TableColumn<Person, GenderEnum> genderCol;
@FXML
TableColumn<Person, String> countryCol;
@Override
public void initialize(final URL location, final ResourceBundle resources)
{
this.nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
this.genderCol.setCellFactory(ComboBoxTableCell.forTableColumn(GenderEnum.values()));
this.countryCol.setCellFactory(ComboBoxTableCell.forTableColumn("India", "USA"));
final Person p1 = new Person("A", "M", "India");
final Person p2 = new Person("B", "F", "USA");
final Person p3 = new Person("C", "N", "India");
final Person p4 = new Person("D", null, "USA");
final Person p5 = new Person("E", "M", "India");
final Person p6 = new Person("F", "F", "USA");
final Person p7 = new Person("G", "N", "India");
final Person p8 = new Person("H", null, "USA");
this.table.setItems(FXCollections.observableArrayList(p1, p2, p3, p4, p5, p6, p7, p8));
this.genderCol.setOnEditCommit((event) -> this.doThis(event));
}
private void doThis(final CellEditEvent<Person, GenderEnum> event)
{
System.out.println(event);
System.out.println(event.getOldValue());
System.out.println(event.getNewValue());
System.out.println(event.getRowValue());
}
@FXML
public void printData()
{
this.table.getItems().forEach(System.out::println);
}
}
Person.java
package application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* @author Saravana Kumar M
*
*/
public class Person
{
private final StringProperty name;
private final ObjectProperty<GenderEnum> gender;
private final StringProperty country;
public Person(final String name, final String gender, final String country)
{
this.name = new SimpleStringProperty(name);
if(gender == null)
{
this.gender = new SimpleObjectProperty<>();
}
else
{
this.gender = new SimpleObjectProperty<>(GenderEnum.valueOf(gender));
}
this.country = new SimpleStringProperty(country);
}
public String getName()
{
return this.name.get();
}
public void setName(final String name)
{
this.name.set(name);
}
public StringProperty nameProperty()
{
return this.name;
}
public GenderEnum getGender()
{
return this.gender.get();
}
public void setGender(final GenderEnum gender)
{
this.gender.set(gender);
}
public ObjectProperty<GenderEnum> genderProperty()
{
return this.gender;
}
public String getCountry()
{
return this.country.get();
}
public void setCountry(final String country)
{
this.country.set(country);
}
public StringProperty countryProperty()
{
return this.country;
}
@Override
public String toString()
{
return "name : " + this.name + " name.get : " + this.name.get() + " gender : " + this.gender + " gender.get : " + this.gender.get() + " country : " + this.country.get() + " country.get : " + this.country.get();
}
public enum GenderEnum
{
M("Male"), F("Female"), N("None");
private final String label;
private GenderEnum(final String label)
{
this.label = label;
}
@Override
public String toString()
{
return this.label;
}
}
}
提前致谢。
By default the TableColumn edit commit handler is non-null, with a default handler that attempts to overwrite the property value for the item in the currently-being-edited row. It is able to do this as the
Cell.commitEdit(Object)
method is passed in the new value, and this is passed along to the edit commit handler via theCellEditEvent
that is fired. It is simply a matter of callingTableColumn.CellEditEvent.getNewValue()
to retrieve this value.It is very important to note that if you call
TableColumn.setOnEditCommit(javafx.event.EventHandler)
with your ownEventHandler
, then you will be removing the default handler. Unless you then handle the writeback to the property (or the relevant data source), nothing will happen. You can work around this by using theTableColumnBase.addEventHandler(javafx.event.EventType, javafx.event.EventHandler)
method to add aTableColumn.EDIT_COMMIT_EVENT
EventType with your desired EventHandler as the second argument. Using this method, you will not replace the default implementation, but you will be notified when an edit commit has occurred.
既然你有
this.genderCol.setOnEditCommit((event) -> this.doThis(event));
在您的控制器 class 中,您删除了更新 属性 的默认处理程序。如果将其替换为
this.genderCol.addEventHandler(TableColumn.<Person, GenderEnum>editCommitEvent(),
event -> this.doThis(event));
它将按预期工作。 (请注意文档告诉您如何获取事件类型是不正确的。还要注意您的处理程序将在默认处理程序之前被调用,因此 getRowValue()
的输出将显示值 before 更新,但按下按钮显示值将给出新值。)