我是 JavaFX 新手。我在使用控制器时遇到了麻烦。
我有两个 GUI,称为 A 和 B,每个都有其控制器(ControllerA 和 ControllerB)。
我的程序非常简单:它首先打开 A,然后有一个按钮,按下时打开 B。反之亦然,B 有一个打开 A 的按钮。
ControllerA 有一种方法,称为“openA”,ControllerB 有一种方法,称为“openB”。
因此,A 需要一个 ControllerB 来打开 B,反之亦然。
我看了一个教程,他处理控制器通信的方式如下:
public class ControllerA{
public void onPressingButtonB(ActionEvent e) throws IOException{
FXMLLoader loaderB = new FXMLLoader(getClass().getResource("class-b.fxml"));
root = loaderB.load();
ControllerB controllerB = loaderB.getController();
controllerB.openB(e);
}
但这对我来说似乎“不是最佳”。每次我在 A 并想去 B 时,我都需要重新确认 ControllerB。所以,我声明ControllerA有一个ControllerB,并使用了以下代码:
public class ControllerA{
private ControllerB controllerb;
{
try {
controllerb = loadControllerB();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public ControllerB loadControllerB() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("class-b.fxml"));
root = loader.load();
return loader.getController();
}
public void onPressingButtonB(ActionEvent e) throws IOException{
controllerb.openB(e);
}
这样,我的动作侦听器可以解析为一行,直接在我的类中实例化控制器,并且它的工作方式就像一个魅力。
事情是......当然我需要用ControllerB进行镜面操作,但这会导致一个主要问题:如果ControllerA在创建时实例化一个ControllerB,而ControllerB在创建时实例化一个ControllerA......这是一个循环。事实上,它会循环并在加载方法上给我错误。
我的问题是:有没有一种方法可以修复我的代码并仅创建一次控制器(这样我的动作侦听器可以只是一行代码),或者我每次必须使用控制器时都必须重新实例化控制器?
非常感谢。
对我来说,所描述的设计都不是特别好的设计。控制者不应该在任何意义上公开自己的观点。两个控制器之间通信的事实上的行业标准方式是通过模型(即使用 MVC 设计)。
在这种情况下,您的模型可以包含一些表示当前视图应该是什么的状态。通常,这只是您的模型无论如何都会包含的自然属性的函数。例如,如果您有一个用户必须登录的应用程序,您的模型可能有一个用户属性。登录屏幕的控制器将验证用户凭据并更改用户属性(如果正确)。其他屏幕可能有一个注销按钮,仅将用户设置为空。如果用户更改为空,模型用户属性上的观察者将显示登录屏幕,如果用户更改为非空,则显示主屏幕。
为了简单的演示目的,这里有一个模型类,它直接有一个代表当前视图的属性:
package org.jamesd.examples.switchviews;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class Model {
public enum View {A,B}
private final ObjectProperty<View> currentView = new SimpleObjectProperty<>(View.A);
public View getCurrentView() {
return currentView.get();
}
public ObjectProperty<View> currentViewProperty() {
return currentView;
}
public void setCurrentView(View currentView) {
this.currentView.set(currentView);
}
}
现在您的控制器应该做的就是更新(共享)模型的状态:
A.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jamesd.examples.switchviews.ControllerA"
alignment="CENTER"
spacing="20"
fx:id="root">
<Label text="This is view A"/>
<Button text="Go to view B" onAction="#goToB"/>
</VBox>
和控制器A:
package org.jamesd.examples.switchviews;
import javafx.fxml.FXML;
public class ControllerA {
private Model model;
public void setModel(Model model) {
this.model = model;
}
@FXML
private void goToB() {
model.setCurrentView(Model.View.B);
}
}
同样,B.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jamesd.examples.switchviews.ControllerB"
alignment="CENTER"
spacing="20"
fx:id="root">
<Label text="This is view B"/>
<Button text="Go to view A" onAction="#goToA"/>
</VBox>
和控制器B:
package org.jamesd.examples.switchviews;
import javafx.fxml.FXML;
public class ControllerB {
private Model model;
public void setModel(Model model) {
this.model = model;
}
@FXML
private void goToA() {
model.setCurrentView(Model.View.A);
}
}
模型更改时更改实际视图的责任属于其他地方。在这个简单的示例中,我们可以在应用程序类中执行此操作,该类可以访问场景:
package org.jamesd.examples.switchviews;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
private Parent viewA ;
private Parent viewB ;
@Override
public void start(Stage stage) throws IOException {
Model model = new Model();
// load both views:
FXMLLoader loaderA = new FXMLLoader(getClass().getResource("A.fxml"));
viewA = loaderA.load();
ControllerA controllerA = loaderA.getController();
controllerA.setModel(model);
FXMLLoader loaderB = new FXMLLoader(getClass().getResource("B.fxml"));
viewB = loaderB.load();
ControllerB controllerB = loaderB.getController();
controllerB.setModel(model);
// create scene with initial view:
Scene scene = new Scene(viewFromModel(model.getCurrentView()),320,200);
// change view when model property changes:
model.currentViewProperty().addListener((obs, oldView, newView) ->
scene.setRoot(viewFromModel(newView))
);
stage.setScene(scene);
stage.show();
}
private Parent viewFromModel(Model.View view) {
return switch(view) {
case A -> viewA ;
case B -> viewB ;
};
}
public static void main(String[] args) {
launch();
}
}
root = loader.load();
实例化指定路径的新控制器。所以不,你不能坚持使用同一个实例,因为你将无法访问声明的控制器上的
@FXML
内容,或者你可能正在调用你正在可视化的页面的旧实例。