我正在尝试在JavaFX中构建一个简单的Login面板。我在这里使用几乎相同的方法在stackoverflow上回顾了有关此问题的其他几篇文章,但到目前为止,还无法弄清我做错了什么。我正在使用日食。

在我尝试访问LoginController类中的login变量之前,这部分代码将起作用。在initLogon方法中,该变量具有数据并正在为其分配数据,但是当我尝试从其他位置访问该变量时,它为null。我的猜测是,我正在某种程度上在LoginController类的2个单独实例中工作,一个实例通过initdata初始化,而另一个实例未通过初始化,但是我无法弄清楚我在哪里连接失败了。点。

这是我的代码的相关部分:

登录类别:

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; }
}


LoginController类:

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


}

TestApplication类:

    // 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()。然后返回。

在您的代码中,(必需要)在controller.initLogin()方法完成之后(因此在调用load方法之后)调用initialize()。因此,在login方法中initialize()为null。

一个快速修复方法是执行依赖于login方法中initLogin()的初始化。

可能更强大的修复方法是控制控制器的实例化。您可以通过从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);
    }
}


或者,将fx:controller属性保留在FXML文件中,并使用控制器工厂。如果您想反思地检查控制器类并在存在适当方法或构造函数的情况下设置值,则此方法更有用。但是,对于这种简单情况,它可能看起来像:

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的方法来定义,这会导致更健壮的代码。

07-24 13:44