我使用java 8.0.45。我用数据绑定实现了我的第一个javafx应用程序(非常简单)。但是,根据用户输入 - > pojo似乎与错误一起工作。我检查了大约200次。我在文本字段中输入了新值,之后我检查了模型值。相同的代码,我的行为一样。有时一切正常(大多数情况下 - 约80-90%)有时模型值!=文本字段值。我注意到了以下内容。某些特定文本字段的数据绑定可以正常工作,然后在某个时间点绑定停止工作,并且该特定文本字段的所有新值都不会传递给模型。也不例外。也没有任何警告。没有。只是绑定不起作用。
我有4个textfiled,它们是通过fxml创建的。两个用于字符串模型类型。一个用于整数。一个用于bigdecimal。问题发生在所有这些领域(有时是一个,有时是几个)。由于我的数字字段可以具有空值,因此我使用的是PropertyObject而不是IntegerProperty(来自openjfx的人建议使用)。
这个JavaFx错误还是什么?附:我使用felix osgi,焊接cdi和pax - 我不知道它是否重要......
我的代码如下:
DTO - POJO模型
public class Task {
private String name;
private Integer order;
private BigDecimal weight;
private String comment;
private final PropertyChangeSupport propertyChangeSupport;
public Task() {
this.propertyChangeSupport = new PropertyChangeSupport(this);
}
public String getName() {
return name;
}
public void setName(String name) {
String pv = this.name ;
this.name = name;
propertyChangeSupport.firePropertyChange("name", pv, name);
}
public Integer getOrder() {
return order;
}
public void setOrder(Integer order) {
Integer pv = this.order;
this.order = order;
propertyChangeSupport.firePropertyChange("order", pv, this.order);
}
public BigDecimal getWeight() {
return weight;
}
public void setWeight(BigDecimal weight) {
BigDecimal pv = this.weight;
this.weight = weight;
propertyChangeSupport.firePropertyChange("weight", pv, weight);
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
String pv = this.comment;
this.comment = comment;
propertyChangeSupport.firePropertyChange("comment", pv, this.comment);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
}
适配器
public class TaskAdapter {
private StringProperty nameProperty;
private ObjectProperty<Integer> orderProperty;
private ObjectProperty<BigDecimal> weightProperty;
private StringProperty commentProperty;
public TaskAdapter(Task task) {
try {
nameProperty=new JavaBeanStringPropertyBuilder().bean(task).name("name").build();
orderProperty=new JavaBeanObjectPropertyBuilder<Integer>().bean(task).name("order").build();
weightProperty=new JavaBeanObjectPropertyBuilder<BigDecimal>().bean(task).name("weight").build();
commentProperty=new JavaBeanStringPropertyBuilder().bean(task).name("comment").build();
} catch (NoSuchMethodException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
}
}
public StringProperty getNameProperty() {
return nameProperty;
}
public ObjectProperty<Integer> getOrderProperty() {
return orderProperty;
}
public ObjectProperty<BigDecimal> getWeightProperty() {
return weightProperty;
}
public StringProperty getCommentProperty() {
return commentProperty;
}
}
BigDecimal Converter
public class SimpleBigDecimalStringConverter extends StringConverter<BigDecimal>{
@Override
public String toString(BigDecimal i) {
if (i == null) {
return "" ;
} else {
return i.toString();
}
}
@Override
public BigDecimal fromString(String string) {
if (string.trim().length() == 0) {
return null ;
} else {
try {
return new BigDecimal(string);
} catch (NumberFormatException nfe) {
return null ;
}
}
}
}
IntegerConverter
public class SimpleIntegerStringConverter extends StringConverter<Integer>{
@Override
public String toString(Integer i) {
if (i == null) {
return "" ;
} else {
return i.toString();
}
}
@Override
public Integer fromString(String string) {
if (string.trim().length() == 0) {
return null ;
} else {
try {
return Integer.valueOf(string);
} catch (NumberFormatException nfe) {
return null ;
}
}
}
}
初始化代码
Task task=new Task();
TaskAdapter adapter=new TaskAdapter(task);
nameTextField.textProperty().bindBidirectional(adapter.getNameProperty());
orderTextField.textProperty().bindBidirectional(adapter.getOrderProperty(),new SimpleIntegerStringConverter());
weightTextField.textProperty().bindBidirectional(adapter.getWeightProperty(),new BigDecimalStringConverter());
commentTextField.textProperty().bindBidirectional(adapter.getCommentProperty());
答案 0 :(得分:4)
发生了什么
JavaFX Bindings在幕后使用WeakChangeListener来实现绑定。这意味着如果没有其他对它的引用在范围内,则绑定本身可以被垃圾收集。在您的代码中,adapter
被定义为局部变量,因此在gc运行时的任意时间过早地收集垃圾。
<强>演示强>
这是使用您的代码演示此问题的演示。它具有您定义的相同文本字段以及两个按钮。一个按钮将task
的值转储到控制台,另一个按钮强制垃圾收集器运行。您将看到绑定在您运行gc后立即停止工作。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.converter.BigDecimalStringConverter;
public class POJOBindingExample extends Application {
private TextField nameTextField = new TextField();
private TextField orderTextField = new TextField();
private TextField weightTextField = new TextField();
private TextField commentTextField = new TextField();
@Override
public void start(Stage primaryStage) {
Task task = new Task();
TaskAdapter adapter = new TaskAdapter(task);
nameTextField.textProperty().bindBidirectional(adapter.getNameProperty());
orderTextField.textProperty().bindBidirectional(adapter.getOrderProperty(),new SimpleIntegerStringConverter());
weightTextField.textProperty().bindBidirectional(adapter.getWeightProperty(),new BigDecimalStringConverter());
commentTextField.textProperty().bindBidirectional(adapter.getCommentProperty());
GridPane grid = new GridPane();
grid.addRow(0, new Label("Name:"), nameTextField);
grid.addRow(1, new Label("Order:"), orderTextField);
grid.addRow(2, new Label("Weight:"), weightTextField);
grid.addRow(3, new Label("Comment:"), commentTextField);
Button showButton = new Button("Show Task");
showButton.setOnAction(e -> {
System.out.println(task.getName());
System.out.println(task.getOrder());
System.out.println(task.getWeight());
System.out.println(task.getComment());
System.out.println();
});
Button gcButton = new Button("Run GC");
gcButton.setOnAction(e -> System.gc());
HBox buttons = new HBox(10, showButton, gcButton);
BorderPane.setAlignment(grid, Pos.CENTER);
BorderPane.setAlignment(buttons, Pos.CENTER);
BorderPane.setMargin(grid, new Insets(10));
BorderPane.setMargin(buttons, new Insets(10));
BorderPane root = new BorderPane(grid, null, null, buttons, null);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
<强>修正强>
要解决此问题,您需要确保只要您需要,TaskAdapter
的引用就会持续存在。在上面的代码中,如果您将引用移动到TaskAdapter
以使其成为实例字段,则所有内容都将按要求运行:
public class POJOBindingExample extends Application {
private TextField nameTextField = new TextField();
private TextField orderTextField = new TextField();
private TextField weightTextField = new TextField();
private TextField commentTextField = new TextField();
private TaskAdapter adapter;
@Override
public void start(Stage primaryStage) {
Task task = new Task();
adapter = new TaskAdapter(task);
// ... etc
}
}
您可能也有兴趣阅读Tomas Mikula's blog,但我不认为您可以直接使用他的库实现与POJO的绑定。