我目前有 3 节课。
ScreenController( Controller 类):
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);
但是,我更愿意使用初始实现,因为它更简洁、可读,并且从理论上讲,提供了我想要的确切行为。
最佳答案
您当前遇到的问题有几个:
在您的
LoginScreen
构造函数中,您使用尚未注入(inject)的字段的值调用setWindow
:public LoginScreen() { super(); setWindow(loginWindow); }
在执行 Controller 的构造函数时,不会注入(inject)任何 FXML 字段,这意味着
loginWindow
为null
。原因不言而喻:FXMLLoader
必须首先构造 Controller 实例,然后才能开始注入(inject)适当的字段。事件的顺序是:(1) Controller 实例化,(2) 字段注入(inject),(3) 调用初始化方法;我相信链接任何事件处理程序/更改监听器都包含在第二步中。这意味着需要对 FXML 字段进行的任何初始化都应该在
initialize
方法中完成。您在使用
super(homeWindow)
的HomeScreen
构造函数中遇到了同样的问题,尽管还有其他问题将在下一点中解决。除了尝试访问构造函数中尚未注入(inject)的字段之外,还存在以下两个问题:
@FXML private static AnchorPane homeWindow = new AnchorPane();
第一个问题是初始化一个要注入(inject)的字段。 永远不要这样做。一个好的经验法则是:如果字段用
@FXML
注释,则不要手动为其分配值。 FXML 字段最终将被注入(inject),这意味着您之前分配给它的任何值都将被替换。这可能会导致微妙的问题,因为任何引用先前值的代码都不会使用实际添加到场景图中的对象。另一个问题是您的字段是静态的。 JavaFX 8+ 不支持注入(inject)静态字段。据我了解,它在旧版本中曾经是可能的,但这种行为从未得到正式支持(即是实现细节)。此外,让本质上基于实例的东西(FXML+ Controller )设置一个会影响所有实例的静态字段是没有意义的。
额外的问题:当您将
homeWindow
设置为非静态时,您将无法再使用super(homeWindow)
,因为您无法在调用 super 构造函数之前引用它.
使用两个修改后的类应该允许您的代码运行:
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
方法中,它会导致一个微妙的问题。您将添加一个 root
作为 window
节点的子节点。但是,当发生这种情况时,ScreenController
的新实例(与新的root
关联)具有其window == root
。换句话说,使用 LoginScreen
创建的 window
现在是使用 HomeScreen
创建的 window
的父级。根据更复杂的应用程序的设计方式,这可能会导致“根”的嵌套逐渐加深。
也就是说,您已经有了另一种方法来实际替换整个场景
。正如您所说,您遇到的问题是 Stage
调整大小以适应新的 Scene
。可以通过替换 Scene
的 root
而不是 Scene
本身来解决此问题:
window.getScene().setRoot(root);
<小时/>
一些可能有用的资源:
- Introduction to FXML
- JavaFX FXML Tutorial (javacodegeeks.com)
- JavaFX FXML (jenkov.com)
- what does initialize() mean in javafx?
- JavaFX : Pass parameters while instantiating controller class
- How to swap screens in a javafx-application in the controller-class?
- Loading new fxml in the same scene
- Passing Parameters JavaFX FXML
- Switch between panes in JavaFX
- afterburner.fx
- mvvmFX
- Curated list of JavaFX-related things
关于java - 如何在创建的原始窗口内的 Controller 之间进行转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57580710/