提问者:小点点

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


我目前有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.");
    }
}
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane fx:id="loginWindow" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" opacity="0.5" prefHeight="500.0" prefWidth="850.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.app.client.java.classes.LoginScreen">
   <children>
      <Button fx:id="goButton" layoutX="205.0" layoutY="60.0" mnemonicParsing="false" onAction="#goButtonPressed" text="Button" />
   </children>
</AnchorPane>

主屏幕(辅助屏幕):

import com.app.client.java.controllers.ScreenController;
import javafx.fxml.FXML;
import javafx.scene.layout.AnchorPane;

public class HomeScreen extends ScreenController
{
    @FXML
    private static AnchorPane homeWindow = new AnchorPane();

    public HomeScreen()
    {
        super (homeWindow);
    }
}
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.AnchorPane?>


<AnchorPane fx:id="homeWindow" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.app.client.java.classes.HomeScreen">
   <children>
      <TextArea layoutX="200.0" layoutY="100.0" prefHeight="200.0" prefWidth="200.0" text="aksajkasjkasja" />
   </children>
</AnchorPane>

我希望能够使用setScreen()函数从主屏幕移动到辅助屏幕。但是,我发现该过程没有成功完成。

我发现另一种有效的方法是(尽管它调整窗口大小,而不是用新窗口的内容填充初始窗口):

Parent root = FXMLLoader.load(getClass().getResource("/com/app/client/resources/fxml/" + screen + ".fxml"));
Stage stage = (Stage) loginWindow.getScene().getWindow();
Scene scene = new Scene(root);
stage.setScene(scene);

然而,我更喜欢使用最初的实现,因为它更简洁,可读性更好,并且理论上提供了我想要的确切行为。


共2个答案

匿名用户

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

>

  • 在您的LoginScreen构造函数中,您使用尚未注入的字段的值调用setWindow

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

    在控制器的构造函数执行时,不会注入任何FXML字段,这意味着< code>loginWindow为< code>null。原因不言而喻:< code>FXMLLoader必须首先构造控制器实例,然后才能开始注入适当的字段。

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

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

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

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

    第一个问题是你初始化一个将要被注入的字段。千万不要这样。一个很好的经验法则是:如果字段用< code>@FXML注释,那么不要手动为它赋值。FXML字段最终将被注入,这意味着您之前分配给它的任何值都将被替换。这可能会导致一些微妙的问题,因为任何引用先前值的代码都不会使用实际添加到场景图中的对象。

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

    一个额外的问题:当您使home Window非静态时,您不能再使用超级(home Window),因为您无法在调用超级构造函数之前引用它。

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

    登录屏幕.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);
    

    在您的< code > screen controller # set screen 方法中,它导致了一个微妙的问题。您正在添加一个< code>root作为< code>window节点的子节点。但是当这种情况发生时,< code>ScreenController(与新的< code>root相关联)的新实例具有其< code>window == root。换句话说,用< code >登录屏幕创建的< code >窗口现在是用< code >主屏幕创建的< code >窗口的父窗口。根据更复杂的应用程序的设计方式,这可能导致“根”的嵌套越来越深。

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

    window.getScene().setRoot(root);
    

    一些潜在的有用资源:

      < Li > FXML简介 < li>JavaFX FXML教程(javacodegeeks.com) < li>JavaFX FXML (jenkov.com) < li >在javafx中initialize()是什么意思? < li>JavaFX:实例化控制器类时传递参数 < li >如何在控制器类的javafx应用程序中交换屏幕? < li >在同一场景中加载新的fxml < li >传递参数JavaFX FXML < li >在JavaFX中的窗格之间切换 < li>afterburner.fx < li>mvvmFX < Li > Java FX相关内容的精选列表

  • 匿名用户

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

    我想指出的一个小例子是:

        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开始。请注意中间的部分,它与代码的其余部分有点分离,通过获取加载的场景的控制器,我正在将舞台传递给它。您可以通过这种方式将舞台传递到所有场景。还要注意场景设置的部分,我使用了从舞台上提取的另外两个参数;宽度和高度。除此之外,在初级舞台上运行的几乎每一个场景中,都有更多的方法来获得舞台,只需执行以下操作:

        @FXML private Button aButton;
    
        public Button getAButton(){
           return aButton;
        }
    
        Stage stage = (Stage) getAButton().getScene().getWindow();
    
    

    这将适用于基于主阶段的所有场景,并且只需要在场景图节点中注册一个,无论类型如何。