JavaFX:Task的两个绑定属性

sim*_*ser 4 java java-8 javafx-8

我有一个JavaFX 8应用程序,并希望允许任务修改两个不同的UI元素.据我了解,如果我要修改一个Label,我可以使用mylabel.textProperty().bind(mytask.messageProperty())绑定到Label,并在Task中使用updateMessage().

我怎么能用两种不同的任意类型做到这一点?我已经查看了并发和JavaFX文档中的示例,但对我来说,他们并没有很好地解释这一点.

我理解一个Task本身就有Message(String),Progress(double/long),Title(String)和Value(用户定义)属性,但是如果我想要两个或更多我自己的任意类型属性来控制UI元素呢?(并且希望避免使用runLater().)

我可以在任务上创建任意属性吗?我觉得我错过了一些明显的东西.

jew*_*sea 6

忠告

如果您的任务需要属性样式界面以用于自定义属性,请仅使用下面的解决方案.通常,许多应用程序不需要这样的接口和单个Platform.runLater调用而不是暴露自定义属性就足够了.

您可以使用相同的成语作为用于消息财产一的任务.我只需将相关代码复制并粘贴到此答案中.请注意,此解决方案将通过AtomicReference "合并更新,以便我们不会泛滥事件队列" .此解决方案不违反JavaFX的一般绑定特性,并且如果经常使用,则不会为主线程导致大量消息.但是,因为它会合并更新,所以不是对属性的每次更新都会触发属性更改.每个脉冲最多只触发一次属性更改.

private final StringProperty message = new SimpleStringProperty(this, "message", "");
@Override public final String getMessage() { checkThread(); return message.get(); }
@Override public final ReadOnlyStringProperty messageProperty() { checkThread(); return message; }

/**
 * Used to send message updates in a thread-safe manner from the subclass
 * to the FX application thread. AtomicReference is used so as to coalesce
 * updates such that we don't flood the event queue.
 */
private AtomicReference<String> messageUpdate = new AtomicReference<>();

/**
 * Updates the <code>message</code> property. Calls to updateMessage
 * are coalesced and run later on the FX application thread, so calls
 * to updateMessage, even from the FX Application thread, may not
 * necessarily result in immediate updates to this property, and
 * intermediate message values may be coalesced to save on event
 * notifications.
 * <p>
 *     <em>This method is safe to be called from any thread.</em>
 * </p>
 *
 * @param message the new message
 */
protected void updateMessage(String message) {
    if (isFxApplicationThread()) {
        this.message.set(message);
    } else {
        // As with the workDone, it might be that the background thread
        // will update this message quite frequently, and we need
        // to throttle the updates so as not to completely clobber
        // the event dispatching system.
        if (messageUpdate.getAndSet(message) == null) {
            runLater(new Runnable() {
                @Override public void run() {
                    final String message = messageUpdate.getAndSet(null);
                    Task.this.message.set(message);
                }
            });
        }
    }
}

// This method exists for the sake of testing, so I can subclass and override
// this method in the test and not actually use Platform.runLater.
void runLater(Runnable r) {
    Platform.runLater(r);
}

// This method exists for the sake of testing, so I can subclass and override
// this method in the test and not actually use Platform.isFxApplicationThread.
boolean isFxApplicationThread() {
    return Platform.isFxApplicationThread();
}
Run Code Online (Sandbox Code Playgroud)

其他问题的答案

这是Task类的源代码?

是.这是Task的源代码.

所以你说唯一的方法是使用上面的任务中的其他属性扩展Task类?

好吧,如果你想在可以同时修改的自定义任务中使用自定义属性,那么你需要子类任务.但这与将自定义属性添加到您定义的任何其他类(或扩展另一个现有类以添加属性)实际上没什么不同.唯一的区别是额外的机器,以确保执行在正确的线程上发生,并在需要时合并.

第二个主题,你似乎也在开始时说偶尔调用runLater是一种可以接受的方法吗?

是的,Platform.runLater()是在任务和JavaFX UI线程之间发送消息的推荐方法(如任务javadoc中所示).

这些属性提供了任务和对象之间的松散耦合,这可能依赖于通过观察者模式的任务.如果您不需要松散耦合,那么您不需要特别需要属性(尽管它们有时可以很有用并且很容易绑定,因为JavaFX API的其余部分,例如标签的文本,是基于属性的) .