无法将参数传递给 JavaFx 中的控制器 class

Trouble passing arguments to controller class in JavaFx

我正在尝试使用 JavaFX 构建一个简单的登录面板。我已经在 Whosebug 上使用几乎相同的方法查看了其他几篇关于同一问题的帖子,但到目前为止,我还没有弄清楚我做错了什么。我正在使用日食。

我的这部分代码一直有效,直到我尝试访问我的 LoginController class 中的 login 变量。在 initLogon 方法中,变量有数据并正在分配它,但是当我尝试从其他地方访问变量时,它为空。我的猜测是我以某种方式在 LoginController class 的 2 个独立实例中工作,一个由 initdata 初始化,另一个不是,但我不知道我在哪里未能连接点。

以下是我的代码的相关部分:

登录class:

public class Login {
    private Scene scene;
    private GsonBuilder gsonBuilder;
    private Config config; 
    private Token token; //auth token from json server

    public Login(Scene scene, GsonBuilder gsonBuilder, Config config, Token token){
        this.scene = scene;
        this.gsonBuilder = gsonBuilder;
        this.config = config;
        this.token = token;     
    }
    public void showLoginScreen(){
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("LoginScreen.fxml"));
            scene.setRoot((Parent) loader.load()); 
            LoginController controller = loader.<LoginController>getController();
            controller.initLogin(this);
        } catch (Exception ex) {
            Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public Config getConfig(){ return this.config; }
    public Token getToken(){ return this.token; }
    public GsonBuilder getGsonBuilder(){ return this.gsonBuilder; }
}

登录控制器class:

public class LoginController implements Initializable {

    //Removed FXML stuff for brevity
    private Login login;

    public void initLogin(final Login login){
        this.login = login;
        //Troubleshooting line **works**
        System.out.println("After initLogin, but from initLogin " + 
            this.login.getConfig().getAppUsername());
    }
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        assert txtUsername != null : "fx:id=\"txtUsername\" was not injected: check your FXML file 'LoginScreen.fxml'.";
        assert txtPassword != null : "fx:id=\"txtPassword\" was not injected: check your FXML file 'LoginScreen.fxml'.";
        assert btnLogin != null : "fx:id=\"btnLogin\" was not injected: check your FXML file 'LoginScreen.fxml'.";
        assert btnCancel != null : "fx:id=\"btnCancel\" was not injected: check your FXML file 'LoginScreen.fxml'.";
        System.out.println(this.getClass().getSimpleName() + ".initialize");
        // Troubleshoot line **Does not work**
        System.out.println("During initialization " + 
            this.login.getConfig().getAppUsername());

        //other stuff happens below here
    }
    // Other functions are here

}

测试应用程序class:

    // class extends Application above this (removed for brevity)
    Stage loginStage = new Stage(StageStyle.UNDECORATED);
    Scene scene = new Scene(new AnchorPane());
    Login appLogin = new Login(scene, this.gsonBuilder, this.config, this.token);
    appLogin.showLoginScreen();
    loginStage.setScene(scene);
    loginStage.show();

问题完全在于事情发生的顺序。

当您调用 FXMLLoader.load() 时,FXMLLoader 加载并解析 fxml,创建控制器实例,注入 @FXML-注释字段,并调用 initialize()控制器实例。然后 returns.

在您的代码中,您(必然)在 load 方法完成后调用 controller.initLogin() ,因此在 initialize() 方法完成后已被调用。因此 logininitialize() 方法中为 null。

快速解决方法是在 initLogin() 方法中执行依赖于 login 的初始化。

可能更可靠的修复方法是控制控制器的实例化。您可以通过从 FXML 文件中删除 fx:controller 属性、自己实例化控制器并在 FXMLLoader:

上设置控制器来实现此目的
public void showLoginScreen(){
    try {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("LoginScreen.fxml"));
        LoginController controller = new LoginController();
        controller.initLogin(this);
        loader.setController(controller);
        scene.setRoot((Parent) loader.load()); 
    } catch (Exception ex) {
        Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
    }
}

或者,在 FXML 文件中保留 fx:controller 属性并使用控制器工厂。如果您想反思性地检查控制器 class 并在存在适当的方法或构造函数时设置一个值,则此方法更有用。然而,对于这个简单的例子,它可能看起来像:

public void showLoginScreen(){
    try {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("LoginScreen.fxml"));
        loader.setControllerFactory((Class<?> controllerType) -> {
            if (controllerType == LoginController.class) {
                LoginController controller = new LoginController();
                controller.initLogin(this);
                return controller ;
            } else {
                try {
                    return controllerType.newInstance();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        scene.setRoot((Parent) loader.load()); 
        LoginController controller = loader.<LoginController>getController();
        controller.initLogin(this);
    } catch (Exception ex) {
        Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
    }
}

请注意,这两个示例都允许您使用构造函数参数定义 LoginController,而不是使用专门初始化 Login 的方法,这样可以生成更健壮的代码。