JavaFX ChangeListener并不总是有效
我有一个
JavaFX应用程序,并在那里有一个并发任务.
当Task正在运行时,我想将updateMessage()中的消息附加到TextArea 因为绑定不会将新文本追加到TextArea,所以我使用了ChangeListener worker.messageProperty().addListener((observable,oldValue,newValue) -> { ta_Statusbereich.appendText("n" + newValue); }); 这是有效的,但不是每一次改变. 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,8,9 为什么?有没有其他方法将消息文本附加到TextAres,也许与绑定? 解决方法
message属性被设计为保存任务的“当前消息”的属性:即,目标用例类似于状态消息.在这个用例中,是否永远不会截获存储在属性中的消息只是非常短暂的时间并不重要.的确,documentation for
updateMessage() 表示:
(我的重点).因此,简而言之,传递给updateMessage(…)的某些值可能永远不会被设置为messageProperty的值,如果它们被另一个值快速取代的话.通常,每次将帧渲染到屏幕时(每秒60次或更少),您只能观察到一个值.如果你有一个重要的用例,你想要观察每个值,那么你需要使用另一种机制. 一个非常天真的实现只会使用Platform.runLater(…)并直接更新文本区域.我不推荐这种实现方式,因为您可能会因为调用过多而导致FX应用程序线程泛滥(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应用程序线程,您应该每帧渲染一次消息来自队列的消息不超过一次呈现给屏幕.您可以使用 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(); } } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |