当前位置: 首页 > 面试题库 >

JavaFX ChangeListener并非始终有效

令狐声
2023-03-14
问题内容

我有一个JavaFX应用程序,并且有一个并发任务。在任务运行时,我想将来自updateMessage()的消息附加到TextArea

因为绑定不会将新文本追加到TextArea,所以我使用了ChangeListener

worker.messageProperty().addListener((observable, oldValue, newValue) -> {
    ta_Statusbereich.appendText("\n" + newValue);
});

这是可行的,但并非每一项更改都有效。我用System.out.println()检查了一下,并将任务从1计数到300

for (Integer i = 1; i <= 300; i++) {
    updateMessage(i.toString());
    System.out.println(i.toString());
}

任务中的这个println()给我我想要的1,2,3,4,5,6,7,8等等,但是我的TextArea显示1,4,5,8,9然后我在其中添加了一个println
ChangeListener并获得相同的结果1,4,5,8,9(结果是随机的,并不总是1,4,5 …)

为什么呢?还有其他方法可以将消息文本附加到TextAres,也许可以使用bind?


问题答案:

message属性被设计为包含的“当前消息”的属性,task即目标用例类似于状态消息。在此用例中,仅在非常短的时间内存储在属性中的消息不会被截取并不重要。实际上,有关updateMessage()状态的文档:

对updateMessage的调用被合并,并稍后在FX应用程序线程上运行,因此,即使从FX
Application线程对updateMessage的调用也不一定会导致对该属性的立即更新,并且 中间消息值可能会被合并以保存事件通知

(我的重点)。因此,简而言之,如果传递给它们的某些值很快被另一个值所取代updateMessage(...)messageProperty则可能实际上不会设置为这些值。通常,每次将一帧渲染到屏幕上时,您都只能观察到一个值(每秒60次或更少)。如果您有一个重要的用例,那就要观察每个值,那么您就需要使用另一种机制

一个非常幼稚的实现将只使用Platform.runLater(...)并直接更新文本区域。我不推荐这种实现方式,因为您冒着太多调用淹没FX
Application Thread的风险(updateMessage(...)合并调用的确切原因),使UI 无法响应。但是,此实现如下所示:

for (int i = 1 ; i <= 300; i++) {
    String value = "\n" + i ;
    Platform.runLater(() -> ta_Statusbereich.appendText(value));
}

另一种选择是使每个操作成为一个单独的任务,并在某个执行程序中并行执行它们。附加到每个任务onSucceeded处理程序中的文本区域。在此实现中,结果的顺序不是预先确定的,因此,如果顺序很重要,则这不是适当的机制:

final int numThreads = 8 ;
Executor exec = Executors.newFixedThreadPool(numThreads, runnable -> {
    Thread t = Executors.defaultThreadFactory().newThread(runnable);
    t.setDaemon(true);
    return t ;
});

// ...

for (int i = 1; i <= 300; i++) {
    int value = i ;
    Task<String> task = new Task<String>() {
        @Override
        public String call() {
            // in real life, do real work here...
            return "\n" + value ; // value to be processed in onSucceeded
        }
    };
    task.setOnSucceeded(e -> ta_Statusbereich.appendText(task.getValue()));
    exec.execute(task);
}

如果要通过单个任务完成所有这些操作并控制顺序,则可以将所有消息放入中BlockingQueue,从阻止队列中提取消息,并将其放置在FX
Application线程的文本区域中。为确保您不会在FX
Application线程中进行过多的调用,每帧渲染到屏幕上的消息应最多消耗一次队列中的消息。您可以AnimationTimer为此使用一个:handle保证每个帧渲染都调用一次它的方法。看起来像:

BlockingQueue<String> messageQueue = new LinkedBlockingQueue<>();

Task<Void> task = new Task<Void>() {
    @Override
    public Void call() throws Exception {
        final int numMessages = 300 ;
        Platform.runLater(() -> new MessageConsumer(messageQueue, ta_Statusbereich, numMessages).start());
        for (int i = 1; i <= numMessages; i++) {
            // do real work...
            messageQueue.put(Integer.toString(i));
        }
        return null ;
    }
};
new Thread(task).start(); // or submit to an executor...

// ...

public class MessageConsumer extends AnimationTimer {
    private final BlockingQueue<String> messageQueue ;
    private final TextArea textArea ;
    private final numMessages ;
    private int messagesReceived = 0 ;
    public MessageConsumer(BlockingQueue<String> messageQueue, TextArea textArea, int numMessages) {
        this.messageQueue = messageQueue ;
        this.textArea = textArea ;
        this.numMessages = numMessages ;
    }
    @Override
    public void handle(long now) {
        List<String> messages = new ArrayList<>();
        messagesReceived += messageQueue.drainTo(messages);
        messages.forEach(msg -> textArea.appendText("\n"+msg));
        if (messagesReceived >= numMessages) {
            stop();
        }
    }
}


 类似资料:
  • 我在Android上使用Firebase云消息,遇到了以下问题。当处理应用程序在后台时收到的通知时发生。文件说明如下: 我正在测试的通知是一个显示消息通知(不是数据通知),它也有一个有效负载。

  • 我对Java非常陌生,在仔细阅读文档之后,我发现自己陷入了困境。 我有一个使用JavaFXMediaPlayer播放wav文件的小程序。我的播放器对象有一个currentTimeProperty,我希望在播放期间以分钟:秒为单位显示该属性的输出。 所以我在一个函数的末尾有这样一个函数,它接收我的wav文件并初始化播放器: 然后我有: 这很有效。在wav播放过程中,my以毫秒为单位随当前时间更新。问

  • 下面是片段。请注意,我已经恢复到以前的提交,因此丢失了最近的修改,但请查看我编写的代码,然后才注意到始终等于0() 以下是当检测到活动后按: 以下是NoteActivity接收结果调用的方式。 null 我在我的项目上浪费了很多重要的时间,只是想知道是什么使resultCode和requestCode的值丢失了我发送的值。 任何帮助和指导都将不胜感激。非常感谢!

  • 我正在使用power shell控制台运行以下power外壳脚本: 这将打印错误。然后继续并打印“创建的新对象”和“结束”。所以我假设这是一个非终结性错误。 但是,如果我将try catch块放在新对象周围,如下所示: 在这种情况下,捕获块被击中并写入异常消息。 我的问题是: < li >这是一个非终止错误吗?这看起来像是一个非终止性错误,因为在错误之后继续执行(当没有try catch块时)。

  • 问题内容: 我正在尝试将java8 forEach循环内的布尔变量更改为true,这是非最终的。但是我遇到了以下错误:在封闭范围内定义的必需局部变量必须是final或有效的final。 如何解决这个错误? 代码: 这是我在函数中创建的变量。 现在,当我尝试更改它时: 我收到错误消息:封闭范围中定义的必需局部变量必须是final或有效的final。 为什么会出现此错误,以及如何解决? 问题答案: 您

  • 问题内容: 遇到这个问题(有点烦人),我正在尝试查找集合中的所有记录,而不显示(或显示)特定字段(得分)。这只是一个示例,并不能反映我的实际代码,但是问题总是可以重现的。我要排除的字段非常大,我只是试图生成可用记录的菜单。 像这样的命令 始终返回每个字段,而不是触发mongodb中的排除/包含。我不需要担心什么,因为模板可以控制哪些数据呈现为html?仍然感觉无论如何数据都已传输到客户端。并显示在