JavaFX中现有Java bean的异步绑定

时间:2014-12-09 13:28:29

标签: java asynchronous javafx javafx-8

我正在将现有的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);
    }
}

1 个答案:

答案 0 :(得分:1)

到目前为止我最好的解决方案

  • 创建一个代理ObservableValue
  • 的实用程序类AsyncBinding
  • 听取底层的observable,以正确的线程发送给侦听器。

用法

field.textProperty().bind(
        AsyncBinding.bind(JavaBeanIntegerPropertyBuilder.create()
                .bean(bean).name("x").build().asString("%03d")));

AsyncBinding

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