JavaFX2 Scenes in separated classes - java

Is there a way to keep my scenes in separate Java files in a JavaFx application?
I tried something like this:
public class MyApp extends Application
{
private void init(Stage primaryStage)
{
Group root = new Group();
primaryStage.setResizable(false);
Login login = new Login(root, primaryStage); // from another file
primaryStage.setScene(login);
}
I have to close my login scene after authentication and load another scene from another file, so I'm passing the primaryStage as a parameter for my login Scene to use stage.close()
Is there a better way for doing that?
My Login scene file
public class Login extends Scene
{
public Login(Group root, final Stage stage)
{
super(root, 265, 390, Color.web("EBE8E3"));
Is there another way to reference the current scene stage?

You don't have to pass the stage as a parameter. The stage is always available from the nodes of the current scene !
scene.getWindow()
This returns the current stage/window of the scene !
Javadocs : http://docs.oracle.com/javafx/2/api/javafx/scene/Scene.html#getWindow%28%29
Example : How to get parent Window in FXML Controller?
http://blog.crisp.se/2012/08/29/perlundholm/window-scene-and-node-coordinates-in-javafx

Related

Run method in FXML Controller on KeyEvent

I have a function that updates a ListView in my FXML Controller Class. I want this to run every time the user presses F5.
I'm not sure what the best way is to achieve this and tried following:
1. Get the scene from the controller
I tried to get the scene like here and added scene.onKeyPressed(e -> ...);. But I failed to find a way to get the scene reliably.
2. Call the function from outside
Furthermore I tried to handle this from my scene controller, not my preferred way, because I don't want to call this method when this particular file is not loaded. I load the FXML file with layout.setCenter(FXMLLoader.load(...)); I failed to get an instance of the Controller itself, where I could call the method.
What is wrong with my design? Or is there an #FXML annotation that allows me to handle a KeyEvent?
Example
ApplicationManager:
#Override
public void start(Stage stage){
BorderPane layout = new BorderPane();
Scene scene = new Scene(layout);
layout.setCenter(FXMLLoader.load(getClass().getResource("/designs/lobby.fxml");
}
LobbyFxmlController:
#FXML private ListView<Label> lobbyListView;
#FXML
public void initialize(){
//I can't get the scene here
}
private void loadLobbies(){
// I need to run this on F5 presses
lobbyListView.setItems("lobby 1", "lobby 2", "lobby 3");
}
I just needed to add onKeyPressed="#handleKeyPress" to the FXML layout item and handle this method in the Controller.

getChildren method in Parent - JavaFX

I wrote a code that display a window in JavaFX,
and I loaded XML file that gives me a number of Buttons that I need to create in run-time.
I used Parent as the root and I can't do Parent.getChildren().add(...)
as Pane.getChildren().add(...)
public void start(Stage primaryStage) throws Exception
{
FXMLLoader fxmlLoader = new FXMLLoader();
URL url =
this.getClass().getResource("/resources/UI.fxml");
fxmlLoader.setLocation(url);
Parent root = (Parent)fxmlLoader.load(url.openStream());
primaryStage.setTitle("N In A Row");
primaryStage.setScene(new Scene(root, 1000, 800));
m_GameController =
(MainController)fxmlLoader.getController();
primaryStage.show();
}
Use scene builder to add a container to your application then add the container variable in your application controller class. Now use the created container and add as much child as you want in it through the controller.

JavaFX - opening multiple windows on application startup

There are a couple questions on how to open a new window on pressing a button, but I'd like to open two windows when the application is launched.
My current approach is to put the following code in a new class that functions as the controller of the new window:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("secondWindow.fxml"));
fxmlLoader.setController(this);
try {
parent = (Parent) fxmlLoader.load();
scene = new Scene(parent, 500, 400);
stage = new Stage(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
This works great for buttons or event based openings of windows, I am looking for a simultaneous launching of the two windows. Therefore I'd like to launch my second window from the class with the main method.
In this class you can find the first window being launched using this code:
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
Right below I'd like to add code to launch the second window. I tried:
Parent secondRoot = FXMLLoader.load(getClass().getResource("secondWindow.fxml"));
Scene secondScene = new Scene(secondRoot);
Stage secondStage = new Stage();
secondStage.setScene(secondScene);
secondStage.show();
which to my understanding should do it, but it gives the following error:
java.lang.NoSuchMethodException: monopolybank.SecondWindowController.<init>()
at java.lang.Class.getConstructor0(Class.java:2971)
at java.lang.Class.newInstance(Class.java:403)
How can I fix my approach or what are alternatives to get the same result?
Your problem is nothing to do with the number of windows and all to do with a constructor with parameters that you added to the monopolybank.SecondWindowController class that you created => remove the constructor from that class.

Reloading an Already Populated AnchorPane

So I have been working on a JavaFX project where I have a pretty simple work flow. I have several different AnchorPanes that I use, on each the user will fill out some information and go on to the next AnchorPane. Whenever they move on to the next AnchorPane I load the new AnchorPane using the following code.
private Initializable replaceSceneContent(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream in = MyClass.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(Caster.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
Scene scene = new Scene(page, APP_WIDTH, APP_HEIGHT);
stage.setScene(scene);
stage.sizeToScene();
return (Initializable) loader.getController();
}
This loads everything fine. But I also have the ability to go back to the previous AnchorPane. Currently this uses the same method, but this is not ideal as it loads a brand new AnchorPane and all the information that user had previously entered is gone. I would like to persist the anchorpanes and reload them and have tried to reload them using code similar to the following
private void replaceSceneContent(AnchorPane page) throws Exception {
Scene scene = new Scene(page, APP_WIDTH, APP_HEIGHT);
stage.setScene(scene);
stage.sizeToScene();
}
However, this results in a totally white screen on my application, maybe because I am not loading the FXML again? I need some ideas on how to persist the data the users entered if they want to go back. Any ideas on how to do this well?

JavaFX class controller scene reference

Is there any way of getting the Scene object of an FXML loaded file from the associated class controller.
I'm doing something like this:
#FXML
private AnchorPane anchor;
Scene scene = anchor.getScene();
but i'd like a solution that does not reference the AnchorPane control.
Why not? Controller is an abstract class, he's not aware about UI unless you deliberately make him know.
Nodes (inlcuding AnchorPane) are another story, they hardly exists outside for scenegraph. So it's perfectly fine to ask Node about his parent or scene.
If you still want to handle that separately there are next approaches:
you can create a custom controller and set scene after loader. Just note that at the time initialize() called it wouldn't yet initialized.
public class MyController {
private void Scene scene;
public void setScene(Scene scene) { this.scene = scene; }
}
// loading code
FXMLLoader fxmlLoader = new FXMLLoader();
AnchorPane root = (AnchorPane) fxmlLoader.load(getClass().getResource("MyApp.fxml"));
MyController myController = (MyController) fxmlLoader.getController();
myController.setScene(scene);
You can create a custom fxml control which will incorporate controller and he can just call getScene() for itself. See an example here: https://stackoverflow.com/a/10718683/1054140
I tried your answer, but it did not work, I found the reason here:
JavaFX: How to get stage from controller during initialization?
after the comment:
// loading code
don't use the static load method
AnchorPane root=(AnchorPane) FXMLLoader.load(getClass().getResource("MyApp.fxml"));
but instead use instantiated loader's method
AnchorPane root=(AnchorPane) fxmlLoaded.load(getClass().getResource("MyApp.fxml"));

Categories