Loading different FXML files from a controller - java

I apologize as I understand that the question is a bit broad in nature. What I want to achieve is having the ability to load different FXML files (located in different packages) when specific conditions are met, for example when a button is pressed or when a certain condition is satisfied. So far I've managed to load a file when a button is pressed.
#FXML
private AnchorPane rootPane;
#FXML
private Button btn;
#FXML
private void loadLoginWindow(javafx.event.ActionEvent event) throws IOException {
AnchorPane pane = FXMLLoader.load(getClass().getResource("login/MainWindow.fxml"));
rootPane.getChildren().setAll(pane);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
And it works fine but for one button and one handler only, as soon as another is added it stops working, it seems that only one can work at the time. So clearly I'm missing a bunch of important info but every tutorial I've had a look at doesn't address this point because they don't even get there (They all revolve around a single file, or they rely on hiding and showing panes from the same file)
Does anyone have any good tutorial or comprehensive guide to do this kind of things?
Thanks in advance.

What can happen is the link to the controller within the FXML file can be incorrect. Even though the file location may not exist, the application will still build.
<AnchorPane id="AnchorPane" fx:controller="main.MainController">
The pathway in the fx:controller need to be correct.
Cheers!

Related

How to handle several events for the same Node in JavaFX using only one Event Handling class?

everyone. I've been searching for this question but I haven't found it here, so I'll guess it's really simple.
I'm creating a very simple application in JavaFX with a single button. Now I want to handle its events (like when it's pressed or when it's released), but when I see examples over the Internet, they all use anonymous classes (and a different class for each event), which makes the code dirty in my opinion. That's why I want to put the event handlers in a separate class and add them to the button.
The problem is that I don't know if I have to create a different class for every event, which I think isn't cool. So I came up with an idea. In the handle() method of the class I check which type of event is going on and process it.
This is the code
Main class
public class Main extends Application{
Button button;
PruebaEventHandler evhandler;
public Main() {
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("h0i");
button = new Button("Púlsame!");
evhandler = new PruebaEventHandler();
button.addEventHandler(MouseEvent.ANY, evhandler);
StackPane layout = new StackPane();
layout.getChildren().add(button);
Scene scene = new Scene(layout, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
}
EventHandler class
public class PruebaEventHandler implements EventHandler<MouseEvent>{
#Override
public void handle(MouseEvent event){
if(event.getEventType().equals(MouseEvent.MOUSE_PRESSED)){
System.out.printf("Te cogí\n");
}
if(event.getEventType().equals(MouseEvent.MOUSE_RELEASED)){
System.out.printf("\nMe ha soltado!!!");
}
}
}
I don't know if this is very inefficient or bad programming style, but is the only solution I've come up with so far. So I want to ask you if this is a good solution or, if there's a better one, shed your light on me! Thanks beforehand.
There already exists a solution for this, which is creating a JavaFX-project with a FXML-file, Controller and also a Main class. IDEs like IntelliJ and NetBeans have support for letting you create a JavaFX-project, which automatically creates those files for you, but I'm not sure if you have to add a plugin to make it work, that I don't remember.
The FXML-file takes care of the GUI, for example placing a button in a scene, and the easiest way to use it is with a SceneBuilder, which Oracle has, and can also be integrated in your IDE.
If you use FXML you can direct buttons to methods inside your FXML-document, so you don't need to have anonymous classes for event handlers. Instead you make the button call a spesific method in your Controller-class.
Here are a couple of youtube-tutorials that showcase the basics of using JavaFX with FXML:
https://www.youtube.com/watch?v=K7BOH-Ll8_g
https://www.youtube.com/watch?v=LMdjhuYSrqg

Mutiple FXMLs with multiple controllers in one Main FXML

Recently I started learning JavaFX and now something is bothering me and I can’t find a solution to my “problem”. I have found similar questions and couple of solutions to problems like mine, but I couldn’t find one that’s working for me or simply I am doing something wrong. What I want to do is to have one main FXML file with its own FXML Controller Class. Then I want to add (import) other FXML files, which also have controllers, in the main FXML. I tried couple of things, but nothing worked, so I decided to describe what I am doing. First I am creating the Main FXML file with Scene Builder and then I am creating the Controller for the Main FXML. Then I am setting in Scene Builder the controller class for the Main FXML to be the Main Controller (of course…). After that I am doing the same for the second FXML. Then I am trying to import the second FXML to the Main FXML and it works fine, if I haven’t set a controller for the second FXML. If I have however selected a controller for the second FXML before importing it to the Main FXML, I am still able to import the FXML file and save it, but after I try to run the program, I am getting an error. So basically what I am trying to do is to have multiple FXML files with their own controllers in one Main FXML file, which also has a Controller Class. I am not exactly sure that this is possible at all, so please tell me is that possible at all, and if it’s possible, what am I doing wrong. This is my code :
public class MainSceneController implements Initializable {
#FXML
private TextField mainTxtField;
public MainSceneController() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MainScene.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setRoot(this);
try {
fxmlLoader.load();
} catch (IOException exc) {
} }
#FXML
public void buttonActionMethod(ActionEvent event) {
mainTxtField.setText("Button1 is clicked");
}
#Override
public void initialize(URL location, ResourceBundle resources) {
} }
I called the second FXML and the second controller LeftScene and LeftSceneController, so here is the code for the second controller :
public class LeftSceneController implements Initializable {
#FXML
private TextField leftTxtField;
public LeftSceneController() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MainScene.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setRoot(this);
try {
fxmlLoader.load();
} catch (IOException exc) {
}
}
#FXML
public void button2Action(ActionEvent event) {
leftTxtField.setText("Button 2 is clicked");
}
#Override
public void initialize(URL location, ResourceBundle resources) {
} }
And finally, this is the MainClass, in which are the main method and the start method :
public class MainClass extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("MainScene.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Multiple FXMLs in one");
primaryStage.show();
} }
I hope you are getting what I want to do. As I said, I have tried a lot of things and none of them worked how I wanted it to. This version is compiling and it’s running, if I don’t set a controller class for the second FXML before importing it, but as you can expect, the second button from the imported FXML is not doing anything. I would post screenshots, if I could, but I am new here and usually I am only reading, so I am not allowed to post screenshots. Also I tried to post my FXML code, but I was having problems with the system and I couldn't post more than one line of the code.
So… Is it possible to make this work how I want it to or not?
And also if you read all this mess, thanks at least for your time! :)
It has been a while, but finally I found an answer to my question. I found this video on YouTube and it is showing exactly what I needed. Although I found a couple of problems while I was doing the things from the video step by step.
First of all, if I import another FXML file into the main FXML, like in this tutorial, SceneBuilder is importing the FXML like the things from the imported FXML are in the main FXML and this causes problems. What I mean is for example if you have a Button in the imported FXML, when you import it in the main FXML with SceneBuilder, the imported Button appears in the main FXML like a new Button with all the information for it (postion, onClickMethod, etc.) and that it's not how it's supposed to be. This causes errors, because Java is looking for the onClickMethod for the imported button in the Main Controller and not in the Controller of the imported FXML. I don't know why it's different by me and it is not like in the video, but the solution is simple. If you want to import a FXML file into another FXML, you should do it with an editor and you just have to add the following line in the content of the main FXML :
<fx:include fx:id="importedFXML" source="ImportedFXML.fxml" />
The important thing in this case is that the fx:id should be with the same name as the .FXML file, but with a small first letter.
And the other thing, which was shown in the video and which caused problems by me was if you want to have a multiple imported FXML files and you want them to communicate with each other. The video is showing how to do that, but it is not mentioning that the Controller objects of the imported FXML files, which you have to create in the MainController, must have the same names like the fx:id + the word Controller. For example with the fx:id from above, the object should look like this :
#FXML private ImportedFXMLController importedFXMLController
if the ImportedFXMLController is the controller of the importedFXML
So, I hope that this is going to be helpful to someone.

JavaFX getting scene from a controller

I recently started playing around with Java FX, FXML, and scene builder, and I've been trying to add key listeners to one of the controllers for a scene. When I do this though, the key listeners don't work as they should, and I figure it's because they're not focused onto that particular scene. I tried to get access to the scene the controller was part of in order to set it directly, but it comes up that it's part of a null scene.
Is there a way to gain access to the scene that this controller is used in in order to try and assign key event and listeners to that particular scene? Should I go through the rootController which is static throughout the whole application? Or, better yet, is there a simpler way of going about this?
Most examples I see assume that everything is mostly together in a main class or separated amongst a couple of other classes without FXML being brought in, and I'm not sure how to apply their fixes when I have the java controllers, FXML pages, and the main application all separated.
Thanks for any help!
Use any of the controls that is bound in the Controller and use getScene() on it.
Remember not to use it in initialize() as the root element(though completely processed) is still not placed on the scene when initialize() is called for the controller
public class WindowMainController implements Initializable {
#FXML
private Button button;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println(button.getScene()); // Gives you the Scene
}
#Override
public void initialize(URL url, ResourceBundle rb) {
System.out.println(button.getScene()); // Prints null
}
}

Opening multiple windows with JavaFX

I'm trying to open multiple windows with JavaFX, I have an eventlistener that opens a new window when a button is clicked it looks like this:
#FXML
private void joinAction() {
Parent root;
try {
Stage stage = (Stage) joinButton.getScene().getWindow();
stage.close();
root = FXMLLoader.load(getClass().getResource("main.fxml"));
stage = new Stage();
stage.setTitle("TuneUs");
stage.setScene(new Scene(root));
stage.show();
} catch (IOException e) {e.printStackTrace();}
}
the first window opens and the new one opens, but my problem is getting events to work with my second window
in main.fxml I have this line:
<TextField id="chat_bar" onAction="#sendChat" layoutX="14.0" layoutY="106.0" prefHeight="22.0" prefWidth="403.0"/>
Then in my controller class I have this method:
#FXML
private void sendChat() {
System.out.println("test");
}
but Intellij is telling me that; no controller specified for top level element
So, my question is: Do I need to create multiple controller classes or can I use just one for multiple windows if so how?
The recommended approach is to define a controller for each FXML. Since controllers are very lightweight this shouldn't add much overhead. The controller for your main.fxml file might be as simple as
import javafx.fxml.FXML ;
public class MainController {
#FXML
private void sendChat() {
// ...
}
}
I have used this approach with fairly large numbers of FXML files and corresponding controllers in a single project, and have had no issues with managing the code etc. I recommend using a naming convention of the form Main.fxml <-> MainController.
If your controllers need to share data, use the techniques outlined in Passing Parameters JavaFX FXML
As #Vertex points out in the comments, there is an alternative approach provided by the FXMLLoader.setController(...) method. So in your example above, you could do
#FXML
private void joinAction() {
Parent root;
try {
Stage stage = (Stage) joinButton.getScene().getWindow();
stage.close();
FXMLLoader loader = new FXMLLoader (getClass().getResource("main.fxml"));
loader.setController(this);
root = loader.load();
stage = new Stage();
stage.setTitle("TuneUs");
stage.setScene(new Scene(root));
stage.show();
} catch (IOException e) {e.printStackTrace();}
}
#FXML
private void sendChat() {
// ...
}
This approach is fine if you are not setting any fields (controls) via FXML injection (i.e. with an fx:id attribute in the fxml and a corresponding #FXML annotation in the controller). If you are, it will be very difficult to keep track of when those fields have been set. Moreover, if your joinAction handler is invoked multiple times, you will have multiple instances of the node created by main.fxml, but all sharing a single controller instance (and consequently overwriting the same injected fields). Also note that with this approach, your initialize() method will be invoked both when the original fxml file is loaded, and when the main.fxml file is loaded, which will almost certainly cause undesired effects.
One last note: if you have many FXML files, and corresponding controllers, you might want to look at the afterburner.fx framework. This is a very lightweight framework that mandates a naming convention on FXML files and their corresponding controllers, and also provides a (very) easy mechanism for sharing data between them.
You need to add top level element fx:controller. Look at this answer:https://stackoverflow.com/a/41316536/4247308
I create Borderpane, and inside it vbox at left and scene at the center and also implement initializable . Every time i click diff button in vbox, it changes only scene. For every scene, i create scene.fxml file.

Scenebuilder not showing controller fields

There's a weird behaving I'm noticing in SceneBuilder, I successfully attach the scene to the wanted Controller as shown here :
but sometimes it can't detect the fields annotated with #FXML annotation
public class MainViewController
{
EntityManager em;
#FXML
public Parent View;
#FXML
public BorderPane ContentArea;
public MainViewController()
{
}
...
Scenebuilder should give suggestions by showing the available #FXML annotated fields from the controller, sometimes it s working correctly , but sometimes it doesn't, until I close Eclipse reopen it, but don't understand the problem, and it s really handicapping me, is there any explanations ?
I have faced this problem from the other direction. When making changes in Scenebuilder and starting the Java application the changes are not in the GUI. I always have to make a clean first.
I think you are facing a similar problem, so try to clean your workspace. Maybe you have to restart Scenebuilder also.

Categories