【问题标题】:Javafx - Can application class be the controller classJavafx - 应用程序类可以是控制器类吗
【发布时间】:2016-01-23 00:56:00
【问题描述】:

我目前正在自学 JavaFX,我采用了一个简单的示例程序,该程序对视图进行了硬编码,并将其转换为使用 FXML 的程序(主要是为了可以使用 SceneBuilder 来构建 UI)。我没有编写单独的控制器类,而是使用应用程序类(因此有 1 个 Java 文件和 1 个 FXML 文件)。我没有使用initialize() 方法,因为它是一个线性流(显示 UI、填充字段、等待输入)。视图弹出,但随后应用程序出错,因为没有任何控件映射到适当的变量(所以对于@FXML TableView<...> tabletablenull)。

但是我放了一个initialize()方法进行调试,在initialize()时注入控件,在initialize()退出时返回null。

所以问题是,JavaFX 是否将应用程序类的新实例实例化为单独的控制器类?这可以解释为什么变量超出范围。还是其他原因(例如,仅在从 JavaFX 操作回调时才注入控件)?

【问题讨论】:

  • 它还能做什么? FXMLLoader 如何知道该类的实例是否已经存在?即使它可以分辨,它又如何在可能的多个实例中进行选择?

标签: javafx fxml


【解决方案1】:

如果您已将应用程序类定义为 FXML 文件中的控制器,如果我没记错的话,JavaFX 将创建应用程序类的新实例并将新实例用作控制器。因此,您现有的应用程序类的表仍然为 null。

但是,您可以在应用程序类中以编程方式定义控制器以使用您自己的实例:

FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("example.fxml"));
fxmlLoader.setController(this);
Parent root = (Parent)fxmlLoader.load();

【讨论】:

  • 虽然我将以正确的方式重写示例,但我会先尝试一下,只是为了笑。
【解决方案2】:

FXMLLoader 的默认行为是创建控制器类的新实例并将该实例用作控制器。

具体来说,FXMLLoader 做了类似的事情:

  • 读取根 FXML 元素。
    • 如果根 FXML 元素具有 fx:controller 属性,则
      • 如果控制器已经存在,则抛出异常,否则创建指定类的实例1并将其设置为控制器
  • 继续解析 FXML 文件。如果元素具有fx:id 属性,并且存在控制器(通过任何机制),则将这些字段注入控制器。同样,将事件处理程序注册为对控制器实例中方法的调用。
  • 如果控制器存在并且有这样的方法,则在控制器上调用initialize()

所以,你问的问题:

应用程序类可以是控制器类吗

是的,但这可能是个糟糕的主意。如果您只是使用fx:controllerApplication 子类指定为控制器类,那么会创建Application 子类的第二个实例,@FXML-annotated 字段被注入到该第二个实例上,并且initialize() 方法在第二个实例上调用。显然,@FXML 字段永远不会在调用 start(...) 的实例上初始化,并且永远不会在该实例上调用 initialize() 方法。

你的意思可能是:

启动时创建的应用类实例能否用作控制器?

这个问题的答案也是肯定的,而且,除了您打算立即丢弃的非常小的演示程序之外,这也可能是一个非常糟糕的主意。你会这样做

public class MyApp extends Application {

    @FXML
    private Node someNode ;

    public void initialize() {
        // do something with someNode 
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml/file.fxml"));
        loader.setController(this);
        Parent root = loader.load();
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}

请注意,要使用此代码,您的 FXML 文件不得具有fx:controller 属性。

这样做的问题是你没有分离,没有灵活性。 (例如,如果您在某处创建在 FXML 文件中定义的视图的第二个实例,您最终会得到第二个 Application 子类实例,这充其量是违反直觉的(一个应用程序有两个 Application 实例...)。

所以我主张基本上在每种情况下都为控制器使用单独的类。 Application 子类应该包含最少的代码并且应该只用于启动应用程序。

1 这一步其实稍微复杂一点。如果在fx:controller 属性中指定了一个类,并且不存在任何控制器,则FXMLLoader 将检查controllerFactory。如果存在,则将控制器设置为将指定的Class 传递给controllerFactorycall() 方法的结果,否则通过在指定类上调用newInstance() 创建它(有效地调用它的no-参数构造函数)。

【讨论】:

  • "FXMLLoader 的默认行为是创建控制器类的新实例并将该实例用作控制器。"这就是我认为正在发生的事情。我正在尝试快速而肮脏的转换,现在显然没有遵循最佳实践。
  • 再一次,鉴于(在默认设置中)FXMLLoader 拥有的唯一信息是类的名称,我很难看出可能存在任何其他可能的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-03
  • 2015-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多