JavaFX(ML)中是否可以使用单例控制器?

时间:2016-02-24 08:41:52

标签: model-view-controller javafx singleton

我尝试使用单一模式实现一个控制器,就像在网络上描述的那样。

但是由于以下异常,我无法运行应用程序。

java.lang.IllegalAccessException: Class sun.reflect.misc.ReflectUtil can not access a member of class testapp.Controller with modifiers "private"

我猜那是因为构造函数被声明为private。在这一点上,我不明白我做错了什么。

如果我不清楚我的问题是什么,我将描述我将要做的用例。

start(Stage stage)函数afaik内是唯一可以定义onclose事件的地方(如果我错了请纠正我)。在关闭窗口时,需要执行一些清理操作。这些操作在控制器内,我无法访问start() function。因此,我们的想法是将控制器构建为单例,以使单个实例保持活动状态并提供对主类的访问。

由sillyfly建议的链接Creating a Singleton Controller class in JavaFX对我来说似乎不是一种可能的解决方案,因为控制器被传递给模型类而不是主类。模型构造函数也是手动调用的,这与我正在处理的情况不同。

3 个答案:

答案 0 :(得分:1)

要解决您的问题,您有两种可能性。您可以将自己的单例控制器实例提供给自己实例化的FXML加载器 https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/FXMLLoader.html#setController-java.lang.Object-  或者您为FXML加载程序提供了一个知道如何实例化控制器的控制器工厂。 https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/FXMLLoader.html#setControllerFactory-javafx.util.Callback-

答案 1 :(得分:1)

这对我来说就像X-Y problem

将控制器变为单身对我来说毫无意义(对我而言)。在实践中,每个JavaFX控制器都是有状态的,并且需要访问视图状态:在某些时候,您将要检查文本字段中的文本,或组合框中的选定项等,因此您需要引用视图元素(文本字段或组合框等)。因此,在实践中,您需要在控制器实例与FXML文件定义的层次结构实例之间建立1-1对应关系。因此,如果您将控制器设置为单例,那么您只能加载一次FXML文件。

问题在于没有办法强制执行(与单例工作相同的执行级别)“只加载一次FXML文件”。因此,您必须仅通过编程逻辑来强制执行此规则。当然,一旦你这样做,你的编程逻辑现在强制你只有一个控制器实例,所以你有效地实现了同样的事情。如果你通过编程逻辑强制执行此操作,它允许你重用控制器-FXML视图对,如果你以后想;使控制器类成为单例可以防止它,尽管从您的应用程序代码中可以看出这种情况并非如此。因此,使控制器成为单例可以减少重用(以一种在返回代码时不会显而易见的方式),同时不会产生额外的好处。

您的问题中的状态是“您需要”执行此操作,因为您需要访问主要阶段的onClose处理程序中的控制器。我不明白为什么你不能以通常的方式访问控制器:

@Override
public void start(Stage primaryStage) throws IOException {

    FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml/file.fxml"));
    Parent root = loader.load();
    Scene scene = new Scene(root);
    primaryStage.setScene(scene);

    MyControllerClass controller = loader.getController();
    primaryStage.setOnHidden(e -> {
        // do clean-up:
        controller.shutdown();

        // ...
    });

    primaryStage.show();
}

即使你委托将FXML加载到其他类,你必须start方法(引用主要阶段)和加载方法之间定义一些路由FXML,所以你有一个路径可以通过它传递舞台参考或传递控制器参考来达到你需要的。

答案 2 :(得分:0)

在其他班级:

@FXML
private void btnIngresarOnAction() {
    try {
        FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setLocation(getClass().getResource("/fxml/Frame.fxml"));
        Parent rootNode = fxmlLoader.load();
        Stage stage = new Stage();
        stage.setTitle("Frame");
        Scene scene = new Scene(rootNode);
        stage.setOnCloseRequest(event -> FrameController.frame = null);
        stage.setScene(scene);
        stage.show();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}

在单例课程中:

@Lazy
@Component
public class FrameController implements Initializable {

    public static FrameController frame;

    public FrameController() {
        if (frame == null) {
            frame = this;
        } else {
            throw new RuntimeException("Singleton FXML");
        }
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        System.out.println("Initialize");
    }
}