我正在将现有的Swing项目迁移到JavaFX。该项目有许多使用PropertyChangeSupport的旧式Java bean,这些都在各种后台线程上更新。
我想利用bean适配器(javafx.beans.property.adapter)来连接新视图。但是,这会失败,因为FX应用程序线程上的模型未更新。
理想情况下,我希望保留仅更新模型的现有代码,而不是包装在Platform.runLater中
到目前为止,我的解决方案是使用我自己的类AsyncBinding,它接受由bean适配器创建的现有ObservableValue,监听它,并在相应的线程上触发更改,但是这种方法在任何地方添加都会感到麻烦
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);
}
}
答案 0 :(得分:1)
到目前为止我最好的解决方案
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();
}
}