根据this文档,FXML可被视为MVC的“视图”部分。域对象在Java端定义(“模型”)。我找不到“M”和“C”之间的连接 - 我想从控制器修改一些域对象(或启动修改):“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("");
}
}
此代码与其他应用程序代码完全分开。建立这种联系的正确方法是什么?
答案 0 :(得分:2)
不要使用静态FXMLLoader.load(URL)
方法。而是创建一个FXMLLoader
实例。然后,您可以自己实例化控制器并调用setController(...)
,也可以设置控制器工厂。
使用setController(...)
假设你有一些模型类,称之为Model
。定义控制器以引用它:
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...
}
现在,从您的FXML文件中删除fx:controller
属性,而不是执行以下操作:
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 ...>
标签并注入嵌套控制器(可能还需要访问模型),可以改为指定一个控制器工厂,它实际上是一个将控制器类型映射到控制器实例的函数。 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();
请注意,使用此版本:
Model
的单个参数,则它们将接收对同一Model
实例的引用FXMLLoader
,则可以重复使用相同的控制器工厂将相同的Model
实例传递给加载器,这样所有控制器都可以访问相同的{{1}实例。关于控制器工厂的其他想法
控制器工厂是一个非常强大和灵活的机制。例如,定义一个简单地推迟到Spring Model
的控制器工厂将非常容易。这样您就可以为控制器定义接口,只需在FXML文件中指定接口名称即可。然后,您的Spring配置文件可以确定要使用的控制器接口的哪个实现,当然可以为您将模型(和域对象)注入控制器。
另见
如果您正在做很多事情,请查看Adam Bien's afterburner framework。 Adam定义了一个可重用的控制器工厂,它可以加载控制器并检查其中ApplicationContext
个带注释的字段,并将单例实例注入这些字段。这提供了很大的灵活性,因为您可以轻松地向控制器添加更多共享资源。
答案 1 :(得分:1)
您可以创建FXMLLoader
的实例并使用非静态加载方法(load(java.io.InputStream inputStream)
或load()
)。之后,您可以使用getController()
方法获取控制器。