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" ]
Advertisement
Answer
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 } }