且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Javafx实时线程更新

更新时间:2023-02-26 18:05:15

为什么您的代码不起作用

您的代码不起作用的原因是您阻止了FX应用程序主题。

The reason your code doesn't work is that you are blocking the FX Application Thread.

与所有UI工具包一样(几乎?),JavaFX是一个单线程UI工具包。这意味着所有事件处理程序以及UI的所有呈现都在单个线程上执行(称为FX应用程序线程)。

Like (almost?) all UI toolkits, JavaFX is a single-threaded UI toolkit. This means that all event handlers, and all the rendering of the UI, are performed on a single thread (called the FX Application Thread).

在您的代码中,您有一个事件处理程序,运行时间超过一秒,因为它通过调用 Thread.sleep(...)暂停一秒钟。当该事件处理程序正在运行时,无法重绘UI(因为单个线程不能同时执行两项操作)。因此,当按钮文本的值立即更改时,在句柄(...)方法完成运行之前,新值实际上不会在屏幕上呈现。如果在handle方法中有 for 循环,则在整个循环(以及方法中的任何其他内容)完成之前不会呈现任何内容。

In your code, you have an event handler that takes more than a second to run, because it pauses for a second via a call to Thread.sleep(...). While that event handler is running, the UI cannot be redrawn (because a single thread cannot do two things at once). So while the value of the button's text has changed immediately, the new value won't actually be rendered on the screen until the handle(...) method has finished running. If you had a for loop in the handle method, nothing would be rendered until the entire loop (and anything else in the method) had completed.

如何修复

在JavaFX中执行所需操作的最简单方法是使用 时间轴 处理暂停。 时间轴适当地为您管理线程:

The simplest way to do what you want in JavaFX is to use a Timeline to handle the pause. The Timeline manages the threading appropriately for you:

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class CountingButton extends Application {


    @Override
    public void start(Stage primaryStage) {

        Button button = new Button("Count");

        Timeline timeline = new Timeline();
        for (int count = 0; count <= 5 ; count++) {
            final String text = Integer.toString(count);
            KeyFrame frame = new KeyFrame(Duration.seconds(count), event -> 
                button.setText(text));
            timeline.getKeyFrames().add(frame);
        }

        button.setOnAction(e -> timeline.play());

        primaryStage.setScene(new Scene(new StackPane(button), 120, 75));
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

一般来说,用于更改用户界面的外观在特定时间点, JavaFX Animation API (另见教程)非常有用,尤其是时间轴 PauseTransition

In general, for changing the appearance of the user interface at specific time points, the JavaFX Animation API (see also the tutorial) can be useful, especially Timeline and PauseTransition.

执行此操作的低级方法是自己创建 Thread 并暂停该线程。这是更高级的:您需要小心更新FX应用程序线程上的UI,而不是您创建的线程。您可以通过调用调用Platform.runLater(...)来执行此操作:

A "lower-level" way to do this would be to create a Thread yourself and pause in that thread. This is much more advanced: you need to be careful to update the UI on the FX Application Thread, not on the thread you created. You can do this with a call to Platform.runLater(...):

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class CountingButton extends Application {


    @Override
    public void start(Stage primaryStage) {

        Button button = new Button("Start");

        button.setOnAction(e -> {
            Thread thread = new Thread(() -> {
                for (int i = 0; i <= 5 ; i++) {
                    final String text = "Count: "+i ;
                    Platform.runLater(() -> button.setText(text));
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException exc) {
                        exc.printStackTrace();
                    }
                }
            });
            thread.start();
        });

        primaryStage.setScene(new Scene(new StackPane(button), 120, 75));
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

有关JavaFX中线程的更多一般信息,请使用看看这篇文章:使用线程发出数据库请求

For more general information on threading in JavaFX, have a look at this post: Using threads to make database requests