实例化 JavaFX window

Instantiate JavaFX window

我想在它自己的线程中有一个 JavaFX window 对象,我可以从外部实例化和更新它(通常是每秒 10-20 个)。 window 应该可以查看图像并可以从其他类更新。

主类

ImageViewer window = new ImageViewer("Preview");
window.show();
window.updateFrame(image);

ImageViewer 类

public class ImageViewer extends Application{

private StackPane pane;
private ImageView imgView;
private Stage primaryStage;

public ImageViewer() {
}

public void start(Stage primaryStage) {
    this.primaryStage = primaryStage;
    pane = new StackPane();
    imgView = new ImageView();
    pane.getChildren().add(imgView);
    Scene scene = new Scene(pane);
    primaryStage.setScene(scene);
    primaryStage.show();
}

public void updateFrame(Image image) {
    imgView = new ImageView(image);
}

public void show() { Application.launch(); }

不幸的是,构造函数不接受任何参数,当我尝试在此对象中设置任何值时,没有任何反应。这是为什么?

Applicationclass代表了一个完整的应用,它的start(...)方法就是应用启动的方法(你真的应该把start(...)方法想成相当于传统 Java 应用程序中的 main(...) 方法)。您的问题似乎暗示您认为 Application class 代表 window; Stage class代表一个window。但是,据我所知,您的应用程序无论如何只需要一个 window。

当您调用 Application.launch()(或在 Java 8 中执行 JVM,指定一个 Application subclass 作为主要 class), FX 工具包创建 Application class 本身的实例,启动 FX 工具包和 FX 应用程序线程,并调用 Application 实例上的 start(...) 方法,传入 Stage。你真的不应该自己实例化你的 Application subclass;如果这样做,您将拥有一个与调用 start(...) 方法的实例不同的实例。

与几乎所有 UI 工具包一样,JavaFX 是单线程的。 "live" 场景图元素上的所有操作都必须在 JavaFX 应用程序线程上执行。您当然可以有后台线程,但如果他们需要更新 UI,他们应该安排在 FX 应用程序线程上进行更新,方法是调用 Platform.runLater(...) 或利用 javafx.concurrent API。 ("have a JavaFX Window object in its own thread"这个短语对我来说甚至没有意义。线程没有对象,对象存在于堆上。线程只是一个独立的可执行语句序列的抽象。)

因此,如果您希望 JavaFX 应用程序有一个后台线程来定期更新图像视图中的图像,您可以这样做:

// imports omitted

public class MyImageUpdatingApp extends Application {

    @Override
    public void start(Stage primaryStage) {
        Label label = new Label("Main application");
        BorderPane root = new BorderPane(label);
        Scene scene = new Scene(root, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.show();

        startImageUpdateThread();
    }

    private void startImageUpdateThread() {
        ImageView imageView = new ImageView();
        BorderPane root = new BorderPane(imageView);
        Scene scene = new Scene(root, 600, 400);
        Stage imageViewWindow = new Stage()
        imageViewWindow.setScene(scene);
        imageViewWindow.show();
        final int pause = 50 ;
        Thread t = new Thread( () -> {
            while (moreImagesToGet()) {
                Image image = getNextImage() ;
                Platform.runLater( () -> imageView.setImage(image) );
                try {
                    Thread.sleep(pause);
                } catch (Exception exc) {
                    exc.printStackTrace();
                    break();
                }
            }
        });
        t.setDaemon(true); // this thread won't prevent application exit
        t.setName("Image update thread");
        t.start();
    }

    // ...
}

显然,如果需要,您可以将 startImageUpdateThread 中的代码分解为单独的 class。

请注意,此答案只是为了演示生成图像的线程与 UI 之间的关系。在现实生活中,您可能需要一些更复杂的代码才能充分执行此操作;您不可能足够快地生成和显示图像,因此您可能需要一个 BlockingQueue<Image> 作为图像缓冲区,一个线程生成图像,一个线程使用它们。