I have looked into using ControllerFactory a lot, to allow this code to be instantiated from a database, and have cross-controller compatibility. But with my original setup different from others I found online, I found it extremely hard to follow along, and use what would fit into my program from theirs. Any advice on where to start?
Current Controller creation -
// get Main Class package name to get correct files path
String pathRef = mainRef.getClass().getPackage().getName();
// set FXRouter current route reference
currentRoute = route;
// create correct file path. "/" doesn't affect any OS
String scenePath = "/" + pathRef + "/" + route.scenePath;
// Creates controller for route
Controller_Factory cf = new Controller_Factory();
Object controller = cf.CreateController(route.scenePath);
FXMLLoader loader = new FXMLLoader(controller.getClass().getResource(scenePath));
loader.setController(controller);
Parent root = loader.load();
// set window title from route settings or default setting
window.setTitle(route.windowTitle);
// set new route scene
window.setScene(new Scene(root, route.sceneWidth, route.sceneHeight));
// show the window
window.show();
}
Controller Example-
public class BuyController extends Controller {
@FXML
public Button CloseAppButton;
@FXML public Button SwitchToProfileButton;
@FXML public Button SwitchToSellButton;
@FXML public Button SwitchToBuyButton;
@FXML public Button SwitchToMainButton;
@FXML public TextField BuyText;
String AmountBought;
public void initialize (URL location, ResourceBundle resources){
CloseAppButton.setPrefHeight(30);
CloseAppButton.setPrefWidth(56);
SwitchToBuyButton.setPrefHeight(30);
SwitchToBuyButton.setPrefWidth(56);
SwitchToMainButton.setPrefHeight(30);
SwitchToMainButton.setPrefWidth(56);
SwitchToSellButton.setPrefHeight(30);
SwitchToSellButton.setPrefWidth(56);
SwitchToProfileButton.setPrefHeight(30);
SwitchToProfileButton.setPrefWidth(56);
}
public void OnBuyButton (ActionEvent event) {
AmountBought = BuyText.getText();
System.out.println("You have bought " + AmountBought + " of crypto");
BuyText.clear();
}
@Override
public void initilize(URL url, ResourceBundle rb) {
}
}
Current Controller_Factory-
public class Controller_Factory {
private static final Controller_Factory instance = new Controller_Factory();
public static Controller_Factory getInstance() {
return instance;
}
public Object CreateController (String routeScenePath) throws IllegalArgumentException, IOException {
Object controller = null;
switch (routeScenePath) {
case "Buy.fxml":
controller = new BuyController();
break;
case "Error.fxml":
controller = new ErrorController();
break;
case "Home.fxml":
controller = new HomeController();
break;
case "Profile.fxml":
controller = new ProfileController();
break;
case "Sell.fxml":
controller = new SellController();
break;
default:
}
System.out.println(routeScenePath);
return controller;
}
}
How would I pass this info with the said controller? (This is not real code I have, but an example of configuration JSON I want to pass with the controller.)
"HomePage": {
"ValidPages": [
"BuyPage",
"SellPage"
],
"InternalID": "HP"
},
"BuyPage": {
"ValidPages": [
"HomePage"
],
"InternalID": "BP",
"Cryptos": [
"BTC",
"LTC"
]
The controller factory is simply a Callback<Class<?>, Object>
whose call(Class<?> type)
function takes the class defined in the fx:controller
attribute in the FXML file and returns the object to be used as the controller. This is invoked by the FXMLLoader
at the time the FXML is loaded.
I think your question is asking if you can use a controller factory to automatically populate controllers with data that's stored in JSON, which will be read at runtime.
You can do something like this:
public class NavigationInfo {
private final Map<String, PageNavigationInfo> pageInfoPerPage ;
public NavigationInfo(Map<String, PageNavigationInfo pageInfoPerPage) {
this.pageInfoPerPage = pageInfoPerPage;
}
public PageNavigationInfo getInfoForPage(String page) {
return pageInfoPerPage.get(page);
}
}
public class PageNavigationInfo {
private final String internalID ;
private final List<String> validPages ;
private final List<String> cryptos ;
// .... etc
}
public class NavigationControllerFactory implements Callback<Class<?>, Object> {
private final NavigationInfo navigationInfo ;
public NavigationControllerFactory() {
// read and parse JSON and create NavigationInfo instance
}
@Override
public Object call(Class<?> type) {
try {
for (Constructor<?> c : type.getConstructors()) {
if (c.getParameterCount() == 1 && c.getParameterTypes()[0].equals(NavigationInfo.class)) {
return c.newInstance(navigationInfo);
}
}
// no suitable constructor, just use default constructor as fallabck
return type.getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Now just define the fx:controller
attribute in each FXML in the usual way. E.g. for Buy.fxml
do
<BorderPane ... fx:controller="com.yourcompany.yourproject.BuyController">
<!-- ... -->
</BorderPane>
Then
public class BuyController {
private final PageNavigationInfo navInfo ;
public BuyController(NavigationInfo navigationInfo) {
this.navInfo = navigationInfo.getInfoForPage("BuyPage");
}
@FXML
private void initialize() {
// do whatever you need with navInfo
}
}