【问题标题】:JavaFX: two bound properties for TaskJavaFX:Task 的两个绑定属性
【发布时间】:2016-03-15 19:37:43
【问题描述】:

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

如何使用两种不同的任意类型来做到这一点?我查看了并发和 JavaFX 文档中的示例,但对我来说,它们并没有很好地解释这一点。

我了解任务本身具有 Message(String)、Progress(double/long)、Title(String) 和 Value(user defined) 属性,但如果我想要两个或更多任意类型 我自己的用于控制 UI 元素的属性? (并且希望避免使用 runLater()。)

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

【问题讨论】:

  • 这样做的方法是使用Platform.runLater()。为什么要避免显而易见的解决方案?
  • 假设您的意思是使用 runLater 是显而易见的解决方案,似乎使用它违背了 JavaFX 的一般绑定性质,并且如果使用过于频繁,可能会导致主线程产生大量消息。
  • 您可以使用Platform.runLater() 声明一个属性并从 UI 线程更新它。如果您的代码可能经常调用Platform.runLater(),那么来自@jewelsea 的答案显示了一种抑制和合并频繁调用的技术。如果可能的话,另一种选择是使value 属性的类型封装您需要的所有数据,并调用updateValue()(它被实现为在UI 线程上不太频繁地运行)。然后只需使用自定义绑定到valueProperty()

标签: java java-8 javafx-8


【解决方案1】:

建议

仅当您需要在任务中为自定义属性提供属性样式界面时,才使用以下解决方案。通常,许多应用程序不需要这样的接口,只需调用一个 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 的其余部分,例如标签的文本,都是基于属性的) .

【讨论】:

  • 这是Task类的源代码?所以你说唯一的方法是用额外的属性来扩展Task类,就像上面的Task一样?第二个话题,一开始你似乎也说偶尔调用 runLater 是一种可以接受的方式?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-07
  • 1970-01-01
  • 2014-07-03
  • 2018-08-14
  • 1970-01-01
  • 2010-11-23
相关资源
最近更新 更多