根据this文档 FXML 可以被视为 MVC 的“ View ”部分。域对象在 Java 端(“模型”)定义。我找不到“M”和“C”之间的连接 - 我想从 Controller 修改一些域对象(或启动修改):“M”<-“C”
但是这样的代码与我调用 FXMLLoader.load() 的位置没有关系:
public class FXMLTableViewController {
@FXML private TableView<Person> tableView;
@FXML private TextField firstNameField;
@FXML private TextField lastNameField;
@FXML private TextField emailField;
@FXML
protected void addPerson(ActionEvent event) {
ObservableList<Person> data = tableView.getItems();
data.add(new Person(firstNameField.getText(),
lastNameField.getText(),
emailField.getText()
));
firstNameField.setText("");
lastNameField.setText("");
emailField.setText("");
}
}
此代码与应用程序的其余代码完全分离。建立这种连接的正确方法是什么?
最佳答案
不要使用静态 FXMLLoader.load(URL)
方法。相反,创建一个 FXMLLoader
实例。然后你可以自己实例化 Controller 并调用 setController(...)
,或者您可以设置 Controller 工厂。
使用 setController(...)
所以假设你有一些模型类,将其命名为 Model
。定义您的 Controller 以引用它:
public class MyController {
private final Model model ;
// usual @FXML-annotated fields, etc
public MyController(Model model) {
this.model = model ;
}
public void initialize() { ... }
// handler methods, etc...
}
现在,删除 fx:controller
FXML 文件中的属性,而是执行以下操作:
final Model model = new Model();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("path/to/fxml/file.fxml"));
loader.setController(new MyController(model));
Parent root = loader.<Parent>load();
使用 setControllerFactory(...)
如果您想要或需要使用fx:controller
属性(例如,如果您在 FXML 中使用 <fx:include ...>
标签并注入(inject)嵌套 Controller ,这可能也需要访问模型),您可以指定一个 Controller 工厂,它实际上是一个将 Controller 类型映射到的函数 Controller 实例。 FXMLLoader
将使用它来确定如何从您在 fx:controller
中指定的类名创建对象属性。
例如:
final Model model = new Model();
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml/file.fxml"));
loader.setControllerFactory(new Callback<Class<?>, Object>() {
@Override
public Object call(Class<?> type) {
try {
for (Constructor<?> constructor : type.getConstructors()) {
if (constructor.getParameterCount()==1 &&
constructor.getParameterTypes()[0]==Model.class) {
return constructor.newInstance(model);
}
}
// no matching constructor found, just call no-arg constructor as default:
return type.newInstance();
} catch (Exception exc) {
exc.printStackTrace();
return null ; // bail...
}
}
});
Parent root = loader.<Parent>load();
请注意,在此版本中:
- 包含的任何 FXML 文件都将使用相同的 Controller 工厂,因此如果它们的 Controller 具有采用
Model
类型的单个参数的构造函数, ,他们将收到对相同Model
的引用实例 - 如果您使用多个
FXMLLoader
在您的应用程序中,您可以重用相同的 Controller 工厂来传递相同的Model
实例到加载器,因此所有 Controller 都可以访问相同的Model
实例。
关于 Controller 工厂的其他想法
Controller 工厂是一个非常强大且灵活的机制。例如,定义一个简单地遵循 Spring ApplicationContext
的 Controller 工厂是非常容易的。 。这样您就可以为 Controller 定义接口(interface),并且只需在 FXML 文件中指定接口(interface)名称即可。然后,您的 Spring 配置文件可以确定要使用的 Controller 接口(interface)的实现,当然也可以将模型(和域对象)注入(inject)到 Controller 中。
另请参阅
如果您经常这样做,请查看 Adam Bien's afterburner framework 。 Adam 定义了一个可重用的 Controller 工厂,用于加载 Controller 并检查 @Inject
其中的注释字段,并将单例实例注入(inject)到这些字段中。这提供了很大的灵 active ,因为您可以轻松地向 Controller 添加更多共享资源。
关于java - 从 Controller 访问域对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23414737/