JavaFx:数据绑定(bind)(文本字段)可以在生产模式下使用吗?

标签 java javafx

我使用java 8.0.45。我已经使用数据绑定(bind)实现了我的第一个 javafx 应用程序(非常简单)。然而,从用户输入-> pojo 进行投标似乎存在错误。我已经检查了大约200次。我在文本字段中输入了新值,然后检查了模型值。相同的代码,相同的我的行为。有时一切正常(在大多数情况下 - 大约 80-90%),有时模型值!=文本字段值。我注意到以下几点。某些特定文本字段的数据绑定(bind)有效,有效,然后在某个时间点该绑定(bind)停止工作,并且该特定文本字段的所有新值都不会传递到模型。也不异常(exception)。也没有任何警告。没有什么。仅仅绑定(bind)是行不通的。

我有 4 个文本文件,它们是通过 fxml 创建的。两个用于字符串模型类型。一为整数。一个为 bigdecimal。所有这些字段都会出现问题(有时是一个字段,有时是多个字段)。由于我的数字字段可以有空值,因此我使用 PropertyObject,但不使用 IntegerProperty(openjfx 的人建议这样做)。

那么这是 JavaFx 错误还是什么?附:我使用 felix osgi、weld 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;
    }

}

大十进制转换器

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 ;
            } 
        }
    }
}

整数转换器

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());

最佳答案

发生了什么

JavaFX 绑定(bind)使用 WeakChangeListener在幕后实现绑定(bind)。这意味着如果范围内没有其他对绑定(bind)的引用,则绑定(bind)本身可以被垃圾回收。在您的代码中,适配器被定义为局部变量,因此当 gc 运行时,它会在任意时间过早地进行垃圾收集。

演示

这是一个使用您的代码的演示,显示了该问题。它具有您定义的相同文本字段以及两个按钮。一个按钮将任务的值转储到控制台,另一个按钮强制垃圾收集器运行。一旦运行 gc,您就会看到绑定(bind)停止工作。

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 的绑定(bind)。

关于JavaFx:数据绑定(bind)(文本字段)可以在生产模式下使用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30818499/

相关文章:

java - Swing:JDialogs 如何与 JFrame 对话

java - 如何在Sqlite数据库中存储数据(Android上的JavaFX)

java - Java中堆栈行为的最佳类是什么

java - Android Studio - 目标 API 类中缺少类声明

JavaFx:监听器和/或绑定(bind)处理

JavaFX 我无法在场景生成器中创建的 Canvas 上绘制任何内容

JavaFX 不调用 main(String[] args) 方法

java - 将自定义类型的 FXML 属性设置为自定义 javafx 组件的属性

java - 按其整数内容对 arrayList<class> 进行排序

Java版本错误