我目前有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);
然而,我更喜欢使用最初的实现,因为它更简洁,可读性更好,并且理论上提供了我想要的确切行为。
您目前所拥有的有几个问题:
>
在您的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);
一些潜在的有用资源:
您可以在初始化期间获得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();
这将适用于基于主阶段的所有场景,并且只需要在场景图节点
中注册一个,无论类型如何。