I'm migrating an existing Swing project to JavaFX. The project has a lot of old style Java beans using PropertyChangeSupport, these are all updated on various background threads.
I'd like to make use of the beans adapters (javafx.beans.property.adapter) to hook up new views. However this fails because the models are not updated on the FX application thread.
Ideally I'd like to leave the existing code that updates the models alone rather than wrapping in Platform.runLater
My solution so far is using my own class AsyncBinding that takes an existing ObservableValue created by the beans adapters, listens to it, and fires changes on the appropriate thread, however this feels messy having to add it everywhere
- Is there any built in way of doing this that I'm missing?
- Is there a cleaner approach I could take?
Typical View
public class AsyncIssue extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
FooBean bean = new FooBean();
TextField field = new TextField();
field.textProperty().bind(
JavaBeanIntegerPropertyBuilder.create().bean(bean).name("x")
.build().asString("%03d"));
primaryStage.setScene(new Scene(new VBox(field)));
primaryStage.show();
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
try {
// simulate background work
bean.setX(bean.getX() + 1);
} catch (Throwable e) {
// Executors consume exception by default
e.printStackTrace();
throw e;
}
}, 0, 1, TimeUnit.SECONDS);
}
}
Typical Bean
public class FooBean {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public static final String PROPERTY_X = "x";
private int x;
public int getX() {
return x;
}
public void setX(int x) {
int oldValue = this.x;
this.x = x;
pcs.firePropertyChange(PROPERTY_X, oldValue, x);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
}