为了账号安全,请及时绑定邮箱和手机立即绑定

你如何在创建的原始窗口中的控制器之间转换

你如何在创建的原始窗口中的控制器之间转换

哆啦的时光机 2023-06-14 16:36:53
我目前有3个班级。ScreenController(控制器类):import javafx.fxml.FXMLLoader;import javafx.fxml.Initializable;import javafx.scene.Parent;import javafx.scene.layout.AnchorPane;import java.net.URL;import java.util.ResourceBundle;public class ScreenController implements Initializable{    private AnchorPane window;    public ScreenController()    {        super();    }    public ScreenController(AnchorPane window)    {        setWindow(window);    }    public void setWindow(AnchorPane window)    {        this.window = window;    }    public void setScreen(String screen)    {        try        {            Parent root = FXMLLoader.load(getClass().getResource("/com/app/client/resources/fxml/" + screen + ".fxml"));            window.getChildren().setAll(root);        }        catch (Exception e)        {            e.printStackTrace();        }    }    @Override    public void initialize(URL location, ResourceBundle resources)    {    }}登录屏幕(主屏幕):import com.app.client.java.controllers.ScreenController;import javafx.event.ActionEvent;import javafx.fxml.FXML;import javafx.scene.control.Button;import javafx.scene.layout.AnchorPane;import java.io.IOException;public class LoginScreen extends ScreenController{    @FXML    private AnchorPane loginWindow;    @FXML    private Button goButton;    public LoginScreen()    {        super();        setWindow(loginWindow);    }    @FXML    public void goButtonPressed(ActionEvent event) throws IOException    {        setScreen("Home");        System.out.println("Success.");    }}
查看完整描述

2 回答

?
郎朗坤

TA贡献1921条经验 获得超9个赞

您目前拥有的有几个问题:

  1. 在您的LoginScreen构造函数中,您setWindow使用尚未注入的字段的值进行调用:

    public LoginScreen(){ 
       super();
        setWindow(loginWindow);
    }

    当控制器的构造函数正在执行时,不会注入任何 FXML 字段——意思loginWindownull. 原因不言而喻:必须FXMLLoader先构建控制器实例,然后才能开始注入适当的字段。

    事件的顺序是:(1)实例化控制器,(2)注入字段,(3)调用初始化方法;我相信链接任何事件处理程序/更改侦听器都包含在第二步中。这意味着任何需要发生的关于 FXML 字段的初始化都应该在该initialize方法中完成。

    HomeScreen您在使用 的构造函数中遇到了同样的问题super(homeWindow),尽管还有其他问题将在下一点中解决。

  2. 除了试图访问构造函数中尚未注入的字段外,还有以下两个问题:

    @FXML
    private static AnchorPane homeWindow = new AnchorPane();

    第一个问题是您初始化了一个要注入的字段。永远不要这样做。一个好的经验法则是:如果该字段带有注释,@FXML则不要手动为其分配值。FXML 字段最终将被注入,这意味着您事先分配给它的任何值都将被简单地替换。这可能会导致一些细微的问题,因为任何引用先前值的代码都不会使用实际添加到场景图中的对象。

    另一个问题是你的领域是静态的。JavaFX 8+ 不支持注入静态字段。据我所知,它在旧版本中曾经是可能的,但这种行为从未得到官方支持(即是一个实现细节)。此外,让某些固有的基于实例(FXML+控制器)的东西设置一个会影响所有实例的静态字段是没有意义的。

    一个额外的问题:当你制作homeWindow非静态时,你不能再使用super(homeWindow),因为在调用超级构造函数之前你不能引用它。

使用两个修改后的类应该允许您的代码运行:

LoginScreen.java:


public class LoginScreen extends ScreenController {


    @FXML private AnchorPane loginWindow;

    @FXML private Button goButton;


    @Override

    public void initialize(URL location, ResourceBundle resources) {

        super.initialize(location, resources);

        setWindow(loginWindow); // set window in initialize method

    }


    @FXML

    public void goButtonPressed(ActionEvent event) throws IOException {

        setScreen("Home");

        System.out.println("Success.");

    }


}

HomeScreen.java:


public class HomeScreen extends ScreenController {


    @FXML private AnchorPane homeWindow;


    @Override

    public void initialize(URL location, ResourceBundle resources) {

        super.initialize(location, resources);

        setWindow(homeWindow); // set window in initialize method

    }


}

但是不要使用:


window.getChildren().setAll(root);

在你的ScreenController#setScreen方法中——它会导致一个微妙的问题。您正在添加 aroot作为节点的子节点window。ScreenController但是,当发生这种情况时, (与新的相关联)的新实例root将具有其window == root. 换句话说,windowcreated with现在是created withLoginScreen的父级。根据更复杂的应用程序的设计方式,这可能导致“根”的嵌套越来越深。windowHomeScreen


也就是说,您已经有了另一种方法,您可以实际替换整个Scene. 正如您所说,您在那里遇到的问题是调整Stage大小以适应新的Scene. 这可以通过替换 的root而Scene不是Scene本身来解决:


window.getScene().setRoot(root);


查看完整回答
反对 回复 2023-06-14
?
慕丝7291255

TA贡献1859条经验 获得超6个赞

您可以在初始化期间获得 JavaFX 应用程序的主要阶段。其他场景类应该有一个带有 getter 和 setter 的 Stage 字段,这样您就可以通过它们的 Controller 传递主舞台。至于窗口调整大小,您可以通过在语句中添加getStage().getWidth()和来解决。getStage().getHeight()setScene()


我要指出的一个小例子:


    public class MainClass extends Application {


      @Override

      public void start(Stage stage) throws Exception {


        InputStream sceneStream = MainClass.class.getResourceAsStream("/fxml"+

        "/newScene/main.fxml");

        FXMLLoader loader = new FXMLLoader();

        Parent root = loader.load(sceneStream);

        Scene scene = new Scene(root);

        stage.setTitle("App title");


        NewScene controller = loader.getController();

        controller.setMainStage(stage);


        stage.setScene(scene, stage.getWidth(), stage.getHeight());

        stage.show();


     }


所以上面的函数从创建主阶段的MainClass开始。请注意中间部分与代码的其余部分有点分离,通过获取已加载的控制器,Scene我将阶段传递给它。您可以通过这种方式将舞台传递到所有场景。还要注意设置场景的部分,我在这里使用了两个从舞台中提取的参数;宽度和高度。除此之外,还有更多方法可以在主舞台上运行的几乎每个场景中获得舞台,只需执行以下操作:


    @FXML private Button aButton;


    public Button getAButton(){

       return aButton;

    }


    Stage stage = (Stage) getAButton().getScene().getWindow();


Node这将适用于基于初级阶段的所有场景,并且无论类型如何,只需要在场景图中注册即可。


查看完整回答
反对 回复 2023-06-14
  • 2 回答
  • 0 关注
  • 119 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信