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.
Related
I'm trying to add a functionality to my app which changes the language.
I wrote a following controller for this -> https://pastebin.com/n3nXdAx8
I'm having a problem with starting my JavaFx Application, when I run it I get a following error https://hastebin.com/agakacubuv.js
I tried to fix it following this topic Starting JavaFX from Main method of class which doesn't extend Application
It's my Main class https://pastebin.com/6s8iqcUe
try changing
public static Stage stage=new Stage();
to
public static Stage stage;
You initialize the stage in start() method anyway.
Edit
The second thing is that there is no such key as bundles.main.view.application.title (Main:32). try to use main.view.application.title instead.
I have written a little test application that looks like this:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
public class Test extends Application {
public Test() {
System.out.println("first");
}
#Override
public void init() throws Exception {
System.out.println("second");
super.init();
}
#Override
public void start(Stage primaryStage) throws Exception {
System.out.println("third");
Platform.exit();
}
#Override
public void stop() throws Exception {
System.out.println("fourth");
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}
The output is:
first
second
third
fourth
Now, I am asking myself where the difference is between using the default constructor or the init method for initialising some required things.
Thanks!
P.S.: This question is not duplicate with this one, because my issue does not handle the initialisation of variables. My question is JavaFX specific!
When the Application is launched, the following steps are executed during the launch process:
The instance of the Application subclass that should be launched is created. (Constructor is called)
The init() of that instance is called.
The start method of that instance is called.
See javadoc for Application
Since you exit the application after the start method is called, the stop method of the instance is called.
Note that there is no need to use super.init() and super.stop():
From the javadoc
The init and stop methods have concrete implementations that do nothing.
The differences of constructor, init and start are:
Constructor
If your application is not final the application class could be extended. Since the constructor of the extending class is not complete, it may be dangerous to pass a reference to the class to other classes in this state. Also the instance hasn't been initialized to a point where the getParameters method returns the parameters.
init
Initialisation should generally be done in this method. This method isn't run from the fx application thread, so some things cannot be done from this method, like creating a Stage.
start
Last step in the initialisation. Do things that require to be run from the application thread here. Be careful not to block this thread with long-runing operations however, since this freezes the GUI.
Application Class having init() method which you have override.
The Init method will call immediately after the Test class construction.
Init method is actually an internal method of Application Class. This method is actually initialize the whole FX application components internally.
Application class JavaDoc
it may construct other JavaFX objects in this method.
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
}
}
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.
Background: I've created a JavaFX application, embedded in a Swing frame using JFXPanel. I've been using Eclipse as an IDE. The "Main application" is another class which only serves to create an instance of a class which extends JFXPanel to load my .fxml file when it is instantiated. When executing the main class from Eclipse, all is well, the fx:controller specified in my .fxml file has its initialize() method called (I can tell from changes it makes to the UI on load) and there are no problems.
However, when I package everything into a JAR and try to add my JFXPanel extension class to a Swing JFrame instance, it manages to load the .fxml file just fine-read images, style sheets, etc, and the rest of the code is functioning as expected however the fx:controller's initialize() method is never called. I have no problem accessing the class from other classes inside or outside the jar and I've even tried setting up a ControllerFactory that will return an instance of the Controller as well as trying all sorts of combinations of setting the FXMLLoader's classloader and using both the static and non-static methods of invoking load(). The result is always the same: it will work when launching from the IDE but does not when launching from my packaged jar. I know the jar isn't missing any files because like I said there is no issue finding the class from the Java code and the bundled fxml/css files seem to be loading fine, minus the controller issue.
Anybody ever encounter this before or have any idea what might be going on with the FXMLLoader failing to set the Controller? Could this be a bug of some sort?
I had a similar problem when packaging my JavaFX software into a .jar file. Turned out it was a problem regarding relative path. You're IDE has no issues with this, but then when compiled within a jar it is having issues.
This was resolved using following code to call my .fxml file.
getClass().getClassloader().getResource("/my/view/selector.fxml")
To say that this is the "reason" for your bug, I'm not sure, but this sure stumped me for a while and seems to be pretty much the problem I had.
Original question : Executable Jar limited to one window with JavaFX
I was unable to solve this problem. While the fxml/css files are loading fine and referencing the right controller class, I was still unable to see the initialize() method of the controller class get invoked once everything was packaged up into a jar.
Since the only thing I needed the controller for was to grab the various UI objects defined in the fxml file so that I could do real programming with them, I opted instead to just create a recursive search to look for these individual widgets by their fxml ID [seems to look up 'id' first then 'fx:id' if an 'id' isn't found] in the Scene tree..
//grabs fxml file relative to root of the jar
FXMLLoader loader = new FXMLLoader(ClassLoader.getSystemClassLoader().getResource("app.fxml"));
Parent javaFXRoot = (Parent) loader.load();
public Node findWidgetByID(String id, Parent javaFXRoot)
{
return findObject(root, id);
}
private Node findObject(Parent root, String id)
{
for (Node node : root.getChildrenUnmodifiable())
{
if (node.getId() != null && node.getId().equals(id))
{
return node; // found the node, return it
}
Node retValue = null;
if (node instanceof Parent)
{
retValue = findObject(((Parent) node), id); // recursive search
}
if (retValue != null) //if our node was found by the recursive search, return that
{
return retValue;
}
}
return null;
}
I had the same problem where the initialize() method was called from the IDE, but not from a (shaded) jar.
The problem was that we used ProGuard which was configured to keep protected and public methods. However, the initialize() method was declared private. Therefore it obfuscated the method name, JavaFX couldn't find any appropriately named method and initialize() was never called.
To stop ProGuard from obfuscating your JavaFX annotated methods and fields, include this rule into your proguard.conf:
-keepattributes javafx.fxml.FXML
-keepclassmembers class * {
#javafx.fxml.FXML *;
}
The first line will keep the #FXML annotations, the other rule keeps #FXML annotated class member names.