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

如何使用两种不同的任意类型执行此操作?我已经看过了并发和JavaFX文档中的示例,但对我来说它们并不能很好地解释这一点。

我了解一个Task本质上具有Message(String),Progress(double / long),Title(String)和Value(用户定义)属性,但是如果我想要自己的两个或多个任意类型的属性来控制UI元素怎么办? (并且要避免使用runLater()。)

我可以在任务上创建任意属性吗?我觉得我缺少明显的东西。

最佳答案

忠告

仅在您的任务需要自定义属性的属性样式界面时,才使用以下解决方案。通常,许多应用程序不需要这样的接口,只需一个Platform.runLater调用即可,而不需要公开自定义属性即可。



您可以使用与message propertyTask相同的惯用法。我将复制相关代码并将其粘贴到此答案中。请注意,此解决方案将通过AtomicReference“批量更新,以免我们淹没事件队列”。此解决方案不违背JavaFX的常规绑定性质,并且如果使用频率过高,也不会对主线程产生很多消息。但是,由于它合并更新,因此并非对属性的每次更新都会触发属性更改。每个pulse最多只能触发一次属性更改。

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

回答其他问题

这是Task类的源代码?

是。这是source code from Task

因此,您是说唯一的方法就是使用上面的Task中完成的其他属性扩展Task类?

好吧,如果您想要可以同时修改的自定义任务中的自定义属性,那么是的,您需要对任务进行子类化。但这与将自定义属性添加到您定义的任何其他类(或扩展另一个现有类以添加属性)并没有太大区别。唯一的区别是额外的机制可确保执行在正确的线程上进行,并在需要时合并。

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

是的,推荐使用Platform.runLater()在任务和JavaFX UI线程之间进行消息传递(如Task javadoc所示)。

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

09-30 18:48
查看更多