首页 > 解决方案 > JavaFX 预加载器问题

问题描述

你好亲爱的 Stackoverflow,

我有几个关于 JavaFX 类 Preloader 的问题。经过长期的研究,我没有找到任何合适的解决方案来解决我的问题,我认为这并不是我的代码的问题,而是 JavaFX 本身在 Preloader 类方面的限制。

让我直截了当地说:难道不能使用 FXML 来定义 Preloader 的设计吗?有趣的是,所有教程仅通过从类中创建新实例来指导您创建预加载器。(ProgressBar progressBar = new ProgressBar(); 即)

所以,让我们用这个示例代码来和我一起测试一下:

测试预加载器:

import com.sun.javafx.application.LauncherImpl;
import javafx.application.Preloader;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class TestPreloader extends Preloader {


    @FXML
    ProgressBar progressBar;

    public static void main(String[] args) {
        LauncherImpl.launchApplication(TestApplication.class, TestPreloader.class, args);
    }

    public void initialize() {
        System.out.println("initialize printed");
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.initStyle(StageStyle.TRANSPARENT);
        Parent root = FXMLLoader.load(getClass().getResource("preloader.fxml"));
        Scene scene = new Scene(root);
        scene.setFill(Color.TRANSPARENT);
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.centerOnScreen();
        primaryStage.show();
    }

    @Override
    public void handleProgressNotification(ProgressNotification progressNotification) {
        // progressBar.setProgress(progressNotification.getProgress());
    }

    @Override
    public void handleStateChangeNotification(StateChangeNotification notification) {

        System.out.println(notification.getType().toString());
        switch (notification.getType()) {
            case BEFORE_START:
                //progressBar.setProgress(1);
                break;
            case BEFORE_LOAD: //this is where the TestApplication class will be constructed
                //progressBar.setProgress(1);
                break;

            case BEFORE_INIT:
                //progressBar.setProgress(1);
                break;
        }

    }
}

FXML

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>


<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="TestPreloader">
       <children>
          <ProgressBar fx:id="progressBar" layoutX="174.0" layoutY="141.0" prefWidth="200.0" progress="0.0" />
       </children>
    </AnchorPane>

测试应用

import javafx.application.Application;
import javafx.stage.Stage;


public class TestApplication extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        System.out.println("Application started!");
    }
}

如果您开始,则输出按以下顺序排列:

initialize printed
BEFORE_LOAD
BEFORE_INIT
BEFORE_START
Application started!

现在,正如您所看到的,initialize() 方法被直接调用,然后预加载器的状态发生变化,因此在每次更改时都会弹出通知 - 到目前为止一切都很好。

然而困扰我的是,如果我在初始化方法中调用 progressBar#setProgress ,进度实际上会设置为 1 并且不会抛出任何错误。尽管状态在预加载器初始化之后出现,但 NullPointers 也会出现。

要理解我的意思,请在 handleStateNotification 下取消注释 setProgress(在 BEFORE_LOAD 语句下对我来说最有意义),您会看到 NullPointerException 弹出。

现在,为什么会发生?我能做些什么来解决这个问题?这对我来说没有任何意义。如果 Preloader 初始化了,ProgresssBar 不为 null,那之后怎么转为 null 呢?也许也许有一个单独使用的自己的类的新实例?但这对我来说也没有意义。你为什么会有那个?

我在这个问题上停留了几天,我的意思是我可以去一个时间轴并“硬编码”它,但是我想了解导致这些问题的原因。

TLDR;使用 Preloader 类初始化应用程序后 ProgressBar 变为 null,是什么原因造成的,我该怎么办?

编辑:似乎课程没有在某处重建,idk我真的卡住了哈哈

标签: javajavafx

解决方案


当 FXMLLoader 加载您的 FXML 文件时,它会创建指定控制器的新实例。然而,这并不是我们想要的结果。接收状态回调的对象应该是 FXML 窗口的控制器。

FXMLLoader.load(...)_

FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("preloader.fxml"));
fxmlLoader.setController(this);
Parent root = fxmlLoader.load();

并从 FXML 中删除控制器属性,导致 FXMLLoader 使用给定的控制器实例(接收状态回调的实例)而不是构造指定控制器类的新实例。


推荐阅读