猿问

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

我目前有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.");

    }

}

哆啦的时光机
浏览 120回答 2
2回答

郎朗坤

您目前拥有的有几个问题:在您的LoginScreen构造函数中,您setWindow使用尚未注入的字段的值进行调用:public LoginScreen(){     super();     setWindow(loginWindow); }当控制器的构造函数正在执行时,不会注入任何 FXML 字段——意思loginWindow是null. 原因不言而喻:必须FXMLLoader先构建控制器实例,然后才能开始注入适当的字段。事件的顺序是:(1)实例化控制器,(2)注入字段,(3)调用初始化方法;我相信链接任何事件处理程序/更改侦听器都包含在第二步中。这意味着任何需要发生的关于 FXML 字段的初始化都应该在该initialize方法中完成。HomeScreen您在使用 的构造函数中遇到了同样的问题super(homeWindow),尽管还有其他问题将在下一点中解决。除了试图访问构造函数中尚未注入的字段外,还有以下两个问题:@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);

慕丝7291255

您可以在初始化期间获得 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这将适用于基于初级阶段的所有场景,并且无论类型如何,只需要在场景图中注册即可。
随时随地看视频慕课网APP

相关分类

Java
我要回答