this is a follow on question to my previous question:
List of Methods required to be run in JavaFX Application Thread?
Consider I have a Borderpane class with an init() method which makes the skinning and styles, e.g.:
public class DraggableLabel extends BorderPane {
public void init()
{
Label mainLabel = new Label("Hello");
mainLabel.setStyle(....);
this.setCenter(mainLabel);
}
....
}
I'm still stuggling with the problem that it may happen, that the init() method is not called via JAVAFX Application Thread. This may be no problem if the BorderPane is not yet attached to a scene. However, I cannot assure that the programmer has already attached it to the scene before calling init().
How can I check that this Borderpane has not yet been attached to a scene? (e.g. throw an exception). Would it help to set the Borderpane to "setVisible(false)" in constructor and only set it to setVisible(true) at the very end of the init() method?
Thanks!
Related
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
I have a main app class and a fxmlController class but I am entangled in connecting/organizing(confused how these are called and how I should organize them to make the GUI connected business logic) business logic and GUI. Can someone please tell the order in which following function are called or can anyone help how I should call them?
Main class:
public void Main() //constructor
public static void main() // our main
public void start() //I don't know what it is, what purpose it has
// and where should be called
//Note: In main function there is a call as following
fxmlController class:
public void initialize() //I don't know what it is and what purpose it has
public fxmlController() // which function should be called here....
NOTE: I know about FXMLLoader(); please someone explain
I think of an FXML file and its corresponding controller as a pair that manage the user interface. In larger applications you might have several such FXML-controller pairs that make up different parts of the user interface. Within each pair, the FXML file defines the layout of the UI, and the controller defines the logic (i.e. it typically processes user input, etc).
While you said you "know about FXMLLoader", if you understand that fully you would actually understand some of the other things you asked about, so:
The user interface defined by an FXML file and its controller is loaded into memory by an FXMLLoader. In the simplest setup, the controller is defined by a fx:controller attribute in the root element of the FXML file. When the load() method is called on the FXMLLoader, it:
Loads the FXML file
Creates an instance of the controller class specified by the fx:controller attribute, by calling its no-argument constructor
Sets the value of any #FXML-annotated fields in the controller to the elements defined with matching fx:id attributes
Registers any event handlers mapping to methods in the controller
Calls the initialize() method on the controller, if there is one.
Notice the order of those events: the constructor is called before the #FXML-annotated fields are injected, but the initialize() method is called after. This means you can access (and configure) and #FXML-annotated fields in the initialize() method, but not in the constructor. It is quite common (at least in simple applications) not to define any constructor in the controller classes and just to use the default.
You can have as many FXML/controller pairs in your application as you need/want. Each FXML file should have its own controller class. You can load an FXML file as many times as you need if you want multiple instances of the UI it defines: each time the FXMLLoader will create a new controller instance for you that is associated with the UI element you loaded.
The Application subclass (you called it Main) represents the entire application. You should have only one such class per application and only one instance of it, which is created for you by the FX toolkit.
When you start a FX application (which I'll describe below), the FX toolkit is started. Then an instance of your Application subclass is created, and its init() method is called (if you don't define one, the default implementation does nothing). The FX Application Thread is then started and the Application subclass instance's start() method is called on that thread.
Your start() method should do pretty minimal work. Typically it will load your "main" fxml file, place the resulting UI in a scene, put the scene in the stage, and show the stage. All the logic will be handled by the controller for the FXML file, not by the Application subclass.
In more advanced applications, you might start some background services and/or create some data models in your init() method, and connect them with the controller in the start() method, but the ideas above are the basics.
The actual startup process can happen in a couple of ways. If you are using the standard Oracle JRE, then launching an Application subclass with
java Main
(where Main extends Application) will cause the process above to happen; in other words the FX toolkit is started, an instance of Main is created, its init() method is called, and it's start() method is called on the FX Application Thread.
Other environments (particularly IDEs) are not aware of the JavaFX startup process, and expect the class you are executing to have a public static void main(String[] args) method, like any standard Java application class. To support these environments, it is common for your Application subclass to define a main(...) method which simply calls launch(...) (a static method inherited from Application). The launch method forces the FX toolkit to start, etc. It can only be called once during any application lifetime.
So now you have something like:
package com.example ;
// imports...
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// just load fxml file and display it in the stage:
FXMLLoader loader = new FXMLLoader(getClass().getResource("mainUI.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
// main method to support non-JavaFX-aware environments:
public static void main(String[] args) {
// starts the FX toolkit, instantiates this class,
// and calls start(...) on the FX Application thread:
launch(args);
}
}
Then you would have mainUI.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import java.util.ArrayList?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.MyController">
<Label fx:id="label1"/>
<Label fx:id="label2"/>
</VBox>
and the controller
package com.example ;
// imports...
public class MyController {
#FXML
private Label label1 ;
#FXML
private Label label2 ;
// called by the FXML loader after the labels declared above are injected:
public void initialize() {
// do initialization and configuration work...
// trivial example, could also be done directly in the fxml:
label1.setText("Foo");
label2.setText("Bar");
}
}
The controller is binded in you fxml file or where you call the main view for the first time.
So you can use the fx:controller attribute in the xml or the FXMLLoader#setController() method from your launcher.
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
}
}
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.
The problem I'm having is that when I create a new JavaFX project in NetBeans the main method is ignored, and somehow start() is somehow called and everything is just fine, but any time I try to call start I wind up with an exception. The class I used:
public final class JFXDriver extends Application {
public JFXDriver() {
Application.launch();
}
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("GUI.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
I've attempted to start it using the constructor, but it fails (Throws an Exception) for some reason saying that it is being called more than once, which should be impossible because I've constructed this class from a main method with only the new JFXDriver(); in it.
You are doing it wrong in the code. You should not call launch in your constructor. You should call it in your main and pass the name of the class that extends Application.
This causes the system to call init and then start and thus begins the lifecycle of your applicaton. For a more detailed explanation have a look here: http://codelatte.wordpress.com/2013/11/15/getting-started-with-javafx-hello-world-2/
Are you attempting a Swing and FX interop ?
Alright, I've found a solution to the problem. I added:
public static void start() {
Application.launch();
}
and took out the call to Application.launch() in the constructor. This approach worked. I guess that the JavaFX thread created its own instance of the class leading to the Application.launch() being called more than once. Interestingly, without the one application limit, I wonder if this would have led to a StackOverflowException due to the recursive nature of the call.