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
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);
}
}
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);
}
}
My best solution so far
field.textProperty().bind(
AsyncBinding.bind(JavaBeanIntegerPropertyBuilder.create()
.bean(bean).name("x").build().asString("%03d")));
public class AsyncBinding<T> implements ObservableValue<T> {
private ObservableValue<T> value;
private InvalidationListener invalidationListener;
private ChangeListener<T> changeListener;
private List<InvalidationListener> invalidationListeners = new ArrayList<InvalidationListener>(
1);
private List<ChangeListener<? super T>> changeListeners = new ArrayList<ChangeListener<? super T>>(
1);
public static <T> ObservableValue<T> bind(ObservableValue<T> toWrap) {
return new AsyncBinding<T>(toWrap);
}
private AsyncBinding(ObservableValue<T> value) {
this.value = value;
invalidationListener = new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
Runnable fire = () -> {
synchronized (invalidationListeners) {
for (InvalidationListener listener : invalidationListeners) {
listener.invalidated(observable);
}
}
};
if (Platform.isFxApplicationThread()) {
fire.run();
} else {
Platform.runLater(fire);
}
}
};
value.addListener(invalidationListener);
changeListener = new ChangeListener<T>() {
@Override
public void changed(ObservableValue<? extends T> observable,
T oldValue, T newValue) {
Runnable fire = () -> {
synchronized (changeListeners) {
for (ChangeListener<? super T> listener : changeListeners) {
listener.changed(observable, oldValue, newValue);
}
}
};
if (Platform.isFxApplicationThread()) {
fire.run();
} else {
Platform.runLater(fire);
}
}
};
value.addListener(changeListener);
}
@Override
public void addListener(InvalidationListener listener) {
invalidationListeners.add(listener);
}
@Override
public void removeListener(InvalidationListener listener) {
invalidationListeners.remove(listener);
}
@Override
public void addListener(ChangeListener<? super T> listener) {
changeListeners.add(listener);
}
@Override
public void removeListener(ChangeListener<? super T> listener) {
changeListeners.remove(listener);
}
@Override
public T getValue() {
return value.getValue();
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.