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

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

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

但是,我加入了一个
initialize()
方法进行调试,在
initialize()
中插入控件,然后在退出
initialize()
时返回null


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

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

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

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

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

具体而言,
fxmloader
执行以下操作:

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

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

是的,但这可能是个糟糕的主意。如果您只是使用
fx:controller
应用程序
子类指定为控制器类,则会创建
应用程序
子类的第二个实例,
@FXML
-注释字段被注入到该第二个实例中,并在该第二个实例上调用
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文件中定义的视图的第二个实例,则会得到第二个
应用程序
子类实例,这最多是违反直觉的(一个应用程序有两个
应用程序
实例…)

因此,我主张在基本上所有情况下为控制器使用单独的类。
应用程序
子类应包含最少的代码,并且应仅用于启动应用程序


1这个步骤实际上要复杂一点。如果在
fx:controller
属性中指定了一个类,并且没有控制器存在,则
fxmloader
会检查是否存在错误。如果存在,则控制器被设置为将指定的
传递给
controllerFactory的
call()
方法的结果,否则它是通过对指定类调用
newInstance()
创建的(有效地调用其无参数构造函数)。

它还能做什么?
fxmloader
如何知道该类的实例是否已经存在?即使它可以判断,它如何从可能的多个实例中进行选择?“FXMLLoader的默认行为是创建控制器类的新实例,并将该实例用作控制器。”这就是我所认为的情况。我尝试了一个快速而肮脏的转换,现在显然没有遵循最佳实践。同样,考虑到(在默认设置中)
fxmloader
的唯一信息是类的名称,我很难看出还有其他可能的行为。虽然我将以正确的方式重写示例,我要先试试这个,只是为了露齿而笑。