Не используйте статический метод 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...
}
Теперь удалить 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
(например, если вы используете <fx:include ...>
теги в своих FXML и вводящие вложенные контроллеры, которым также может потребоваться доступ к модели), вы можете вместо этого указать фабрику контроллера, которая фактически является функцией, которая сопоставляет типы контроллеров с экземплярами контроллера. 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 файлы включены будут использовать один и тот же контроллер завод, поэтому, если их контроллеры имеют конструктор, принимающий один параметр типа
Model
, они получат ссылку на тот же пример Model
- Если вы используете в своем приложении несколько
FXMLLoader
s, вы можете повторно использовать тот же самый контроль ller, чтобы передать тот же экземпляр Model
загрузчикам, поэтому все контроллеры могут получить доступ к тому же экземпляру Model
.
Дополнительные мысли на контроллер заводов
Контроллер завод является очень мощным и гибким механизмом. Например, было бы довольно легко определить фабрику контроллера, которая просто отложена до весны ApplicationContext
. Таким образом, вы можете определить интерфейсы для контроллеров и просто указать имя интерфейса в файле FXML. Затем ваш конфигурационный файл Spring может определить, какую реализацию интерфейса контроллера использовать, и, конечно, может ввести модели (и объекты домена) в контроллер для вас.
Также см
Если вы делаете много этого, взгляните на Adam Bien's afterburner framework. Адам определяет фабрику повторного использования контроллера, которая загружает контроллеры и проверяет для них аннотированные поля @Inject
и вводит экземпляр singleton в эти поля. Это дает большую гибкость, так как вы можете легко добавить к своим контроллерам больше общих ресурсов.