I am new to JavaFx and hence I cannot find a solution to solve my problem
Suppose I have following application structure :
- views
- first.fxml
Use an observable StringProperty
in the model:
public class AppModel {
private final StringProperty text = new SimpleStringProperty();
public StringProperty textProperty() {
return text ;
}
public final String getText() {
return textProperty().get();
}
public final void setText(String text) {
textProperty().set(text);
}
}
Make your controllers have access to the model:
public class FirstController {
private final AppModel model ;
@FXML
private TextField textEnter ;
public FirstController(AppModel model) {
this.model = model ;
}
// action event handler for button:
@FXML
private void sendText() {
model.setText(textEnter.getText());
}
}
and
public class SecondController {
private final AppModel model ;
@FXML
private TextArea txtView ;
public SecondController(AppModel model) {
this.model = model ;
}
public void initialize() {
// update text area if text in model changes:
model.textProperty().addListener((obs, oldText, newText) ->
txtView.setText(newText));
}
}
The slightly tricky part now is that the controllers don't have a no-arg constructor, which means the default mechanism for the FXMLLoader
to create them won't work. The easiest way is to set them manually. Remove both the
attributes from the FXML files, and then in your Main
class do
AppModel model = new AppModel();
FXMLLoader firstLoader = new FXMLLoader(getClass().getResource("first.fxml"));
firstLoader.setController(new FirstController(model));
Parent firstUI = firstLoader.load();
FXMLLoader secondLoader = new FXMLLoader(getClass().getResource("second.fxml"));
secondLoader.setController(new SecondController(model));
Parent secondUI = secondLoader.load();
If you prefer to keep the
attributes in the FXML files, you can use a controllerFactory
instead, which essentially instructs the FXMLLoader
as to how to create a controller:
AppModel model = new AppModel();
Callback, Object> controllerFactory = type -> {
if (type == FirstController.class) {
return new FirstController(model);
} else if (type == SecondController.class) {
return new SecondController(model);
} else {
try {
return type.newInstance() ; // default behavior - invoke no-arg construtor
} catch (Exception exc) {
System.err.println("Could not create controller for "+type.getName());
throw new RuntimeException(exc);
}
}
};
FXMLLoader firstLoader = new FXMLLoader(getClass().getResource("first.fxml"));
firstLoader.setControllerFactory(controllerFactory);
Parent firstUI = firstLoader.load();
FXMLLoader secondLoader = new FXMLLoader(getClass().getResource("second.fxml"));
secondLoader.setControllerFactory(controllerFactory);
Parent secondUI = secondLoader.load();
You can make the controller factory even more flexible by using (more) reflection; basically you can implement the logic "if the controller type has a constructor taking an AppModel
, call that constructor, otherwise call the no-arg constructor".
If you are creating a large application which needs to do a lot of this, then you might consider using afterburner.fx, which is a framework that essentially allows you to inject the model into the controllers using annotations.