Posted 11 February 2014 - 12:42 PM



POPULAR

I want to leverage, and assume, your knowledge of Swing to make the transition easier.



I want to, wherever possible and convenient, start from completely empty projects, rather than relying on a specific IDE and its JavaFX templates.

JavaFX - the Swing-like Approach

package fxapplication1; import javafx.application.Application; import javafx.stage.Stage; public class FXApplication1 extends Application { @Override public void start(Stage stage) throws Exception { } }

@Override

throws Exception

main()

package fxapplication1; import javafx.application.Application; import javafx.stage.Stage; public class FXApplication1 extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { } }

package fxapplication1; import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.layout.StackPane; import javafx.scene.Scene; public class FXApplication1 extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { StackPane stack = new StackPane(); Scene scene = new Scene(stack, 300, 250); stage.setTitle("Welcome to JavaFX!"); stage.setScene(scene); stage.show(); } }

stage.setTitle("Welcome to JavaFX!");

Spoiler

package fxapplication1; import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.layout.StackPane; import javafx.scene.Scene; import javafx.event.EventHandler; // these are new import javafx.event.ActionEvent; import javafx.scene.control.Button; public class FXApplication1 extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { StackPane stack = new StackPane(); // the new code... Button btn = new Button(); btn.setText("Sign in"); btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { System.out.println("Sign in button clicked!"); } }); stack.getChildren().add(btn); // .. end of new code. Scene scene = new Scene(stack, 300, 250); stage.setTitle("Welcome to JavaFX!"); stage.setScene(scene); stage.show(); } }



stack.getChildren().add(btn);

getChildren()

final Button btn = new Button(); // needs to be 'final' public void handle(ActionEvent event) { //System.out.println("Sign in button pressed"); btn.setText("Sign in button pressed"); }

This tutorial will ease your transition from Swing to the more modern and capable JavaFX. (By JavaFX I mean JavaFX 2, but I will continue to omit the number.)The tutorial is based on these Oracle tutorials:I am not necessarily attempting to improve on these tutorials, although in places I do provide a bit more detail and explanation. I am just taking a slightly different approach:Having said this, I don't talk about Swing in any great detail, so it is not essential. Experience of building a GUI, even in a different language, will be useful though.The tutoracles use NetBeans and make assumptions about what code its JavaFX templates will create. I prefer to learn about JavaFX entirely from the ground up - to get the full picture!I am using practically the same code as that in the tutoracles. This has the advantage that, if you get stuck at any point, or want a little more information, you can refer to the tutoracles.Once you have completed my tutorial I suggest that you read through the Oracle versions, both to pick-up on any small points I may have omitted and to discover links to other resources and tutorials.Create a new, empty, Java project named JavaFX1,containing a package named fxapplication1 anda class named FXApplication1.The above is just a suggestion as the first version of our application is just a single class, so you can build it anywhere. The second version will only contain between 3 and 5 files.Here is the bare-minimum code to get us started:(Well, it is not quite the minimum because theannotation andcould be considered optional.)is the entry-point for JavaFX applications. However, depending on how we run and package our application we can, and perhaps should, include amethod. This is particularly relevant because we are starting from a blank project, and would be necessary if we were integrating JavaFX into a Swing application.So here is our revised minimum code (although, don't attempt to run it yet..):is the primary object in JavaFX. We might consider this as equivalent to a JFrame, although it is more like a window. We won't let comparisons like this hold us back, so:A Stage requires a. Scene is the container for all content.A scene is a hierarchical graph of nodes. It has a root node (or Parent) which is (or can be) a Pane. In the following code we are using a Using Built-in Layout Panes : Oracle.So here is our revised (new and improved) bare-minimum code:Notice the new imports that have been added. If you are using an IDE such as IntelliJ IDEA or NetBeans it offers to add these imports for you automatically. Personally I'm happy to let the IDE add these imports for me, but I do tidy up the import statements as I progress, otherwise we end up with tonnes of them - one for every object!Be careful to use the javafx versions of the imports, there are often classes with the same names in awt, Swing and elsewhere.Some of this code is not too dissimilar to Swing, which is helpful to us. However, we need to move away from thinking purely in Swing-terms quite quickly. JavaFX is more sophisticated - more flexible and powerful - than Swing. The following link provides a useful overview of the Scene Graph, a glimpse into the possibilities:For the moment, though, I will continue to leverage some of our Swing knowledge to help us get up to speed.We can now run our application, although it doesn't do much yet. From IntelliJ IDEA I can right-click the class in the Project Window on the left and chose Run.. Right-clicking the file's tab also gives this option. The process is similar for other IDEs. (In IntelliJ it actually says "Run.. main()" so it is a good thing that we included this method!)It just displays our form (our GUI/ application) with a title and set size, so it doesn't even warrant a screenshot.We will now add a Button and click event for it, that writes to the console. This is very similar to how we would do this in Swing.The following is the full, edited, code. I won't post the full code every time. I do so on this occasion so that you know where new code will be added. Generally, our new code will be added before this line:Again, take note of the new imports that are required.Similar to Swing? The event-handler code is different, but not enough to be scary. This is different though:The use oftells us that the object-hierarchy in JavaFX is more flexible than Swing. That is, many JavaFX objects can contain (practically/almost) any other JavaFX object.Build, run and test the code..I prefer to change the text on the button, rather than writing to the console, so make these changes: final and inner classes :wikipedia

wiki said:

When an anonymous inner class is defined within the body of a method, all variables declared final in the scope of that method are accessible from within the inner class. For scalar values, once it has been assigned, the value of the final variable cannot change. For object values, the reference cannot change. This allows the Java compiler to "capture" the value of the variable at run-time and store a copy as a field in the inner class. Once the outer method has terminated and its stack frame has been removed, the original variable is gone but the inner class's private copy persists in the class's own memory.

Spoiler

//import javafx.scene.layout.StackPane; // new imports: import javafx.scene.layout.GridPane; import javafx.geometry.Pos; import javafx.geometry.Insets; //StackPane root = new StackPane(); GridPane grid = new GridPane(); grid.setAlignment(Pos.CENTER); grid.setHgap(10); grid.setVgap(10); grid.setPadding(new Insets(25, 25, 25, 25)); // change stack to grid.. grid.getChildren().add(btn); Scene scene = new Scene(grid, 300, 250);



getChildren().add()

Spoiler

import javafx.scene.text.Text; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.PasswordField; import javafx.scene.layout.HBox; import javafx.scene.paint.Color;



Spoiler

import javafx.scene.text.*; import javafx.scene.control.*; import javafx.scene.layout.HBox; import javafx.scene.paint.Color;



Spoiler

grid.setPadding(new Insets(25, 25, 25, 25)); Text sceneTitle = new Text("Welcome"); sceneTitle.setFont(Font.font("Tahoma", FontWeight.NORMAL, 20)); grid.add(sceneTitle, 0, 0, 2, 1); Label userName = new Label("User Name:"); grid.add(userName, 0, 1); TextField userTextField = new TextField(); grid.add(userTextField, 1, 1); Label pw = new Label("Password:"); grid.add(pw, 0, 2); PasswordField pwBox = new PasswordField(); grid.add(pwBox, 1, 2); Button btn = new Button("Sign in"); HBox hbBtn = new HBox(10); hbBtn.setAlignment(Pos.BOTTOM_RIGHT); hbBtn.getChildren().add(btn); grid.add(hbBtn, 1, 4); final Text actionTarget = new Text(); grid.add(actionTarget, 1, 6); btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { actionTarget.setFill(Color.FIREBRICK); actionTarget.setText("Sign in button pressed"); } }); Scene scene = new Scene(grid, 300, 250);



Spoiler

package fxapplication1; import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.event.EventHandler; import javafx.event.ActionEvent; import javafx.scene.control.Button; import javafx.scene.layout.GridPane; import javafx.geometry.Pos; import javafx.geometry.Insets; import javafx.scene.text.*; import javafx.scene.control.*; import javafx.scene.layout.HBox; import javafx.scene.paint.Color; public class FXApplication1 extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { GridPane grid = new GridPane(); grid.setAlignment(Pos.CENTER); grid.setHgap(10); grid.setVgap(10); grid.setPadding(new Insets(25, 25, 25, 25)); Text sceneTitle = new Text("Welcome"); sceneTitle.setFont(Font.font("Tahoma", FontWeight.NORMAL, 20)); grid.add(sceneTitle, 0, 0, 2, 1); Label userName = new Label("User Name:"); grid.add(userName, 0, 1); TextField userTextField = new TextField(); grid.add(userTextField, 1, 1); Label pw = new Label("Password:"); grid.add(pw, 0, 2); PasswordField pwBox = new PasswordField(); grid.add(pwBox, 1, 2); Button btn = new Button("Sign in"); HBox hbBtn = new HBox(10); hbBtn.setAlignment(Pos.BOTTOM_RIGHT); hbBtn.getChildren().add(btn); grid.add(hbBtn, 1, 4); final Text actionTarget = new Text(); grid.add(actionTarget, 1, 6); btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { actionTarget.setFill(Color.FIREBRICK); actionTarget.setText("Sign in button pressed"); } }); Scene scene = new Scene(grid, 300, 250); //grid.setGridLinesVisible(true); stage.setTitle("Welcome to JavaFX!"); stage.setScene(scene); stage.show(); } }



grid.setGridLinesVisible(true);

MVC With FXML

Just as FlowLayout is less useful than other layout-managers in Swing, we will switch from a StackPane to a GridPane , which is similar to Swing's GridBagLayout.provide the padding between components - now called controls or nodes.You can run this code but it won't, yet, be any different to the previous code.will still centre the button on the pane.Rather than changing the text of the button when clicking we will display text elsewhere on the form. We are also going to add a number of other controls and position them on the GridPane.This list of new imports tells us what new controls we will be adding to our form:The imports are becoming unwieldy, let's tidy them a little:The following new code is taken directly from 2 Creating a Form in JavaFX . There was little to be gained by changing it, and it will help you if you later decide to read through these tutorials, which I recommend.Here is the full, current, code:This is all quite similar to Swing. The Button is added to an, which has its alignment set to bottom-right before being added to the grid.Notice that actionTarget is aobject rather than requiring a. This is another (small) indication of the flexibility of JavaFX.Uncomment the lineand run it again. These gridlines are handy but don't leave them visible in the final code!

Quote

JavaFX enables you to design with Model-View-Controller (MVC), through the use of FXML and Java. The "Model" consists of application-specific domain objects, the "View" consists of FXML, and the "Controller" is Java code that defines the GUI's behavior for interacting with the user.

Spoiler

package fxapplication2; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; public class FXApplication2 extends Application { @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("fxapplication2.fxml")); stage.setTitle("Hello MVC"); stage.setScene(new Scene(root, 300, 275)); stage.show(); } public static void main(String[] args) { launch(args); } }



import javafx.fxml.FXMLLoader; import javafx.scene.Parent; Parent root = FXMLLoader.load(getClass().getResource("fxapplication2.fxml")); stage.setScene(new Scene(root, 300, 275));

getResource()

Quote from: Implementing JavaFX Best Practices So far, so Swing-like. We now want to move to the modern MVC ( Model-View-Controller :wiki) pattern, initially by separating the application-logic from the UI-design.Our second version will still not be able to fully demonstrate the MVC pattern, because we do not have any 'model' to speak of. It does, however, exemplify the principle of separation of concerns It was my original intention to modify our existing application to achieve this. However, this isn't really useful as we would need to rip out most of the code. Instead create a new, but still blank/empty, package namedwithin our project, and then a new classI know that modern IDEs have templates for JavaFX that will create the initial code for us, and this is the approach the Oracle tutorials take. I still prefer to create, and discuss, the code from scratch. Besides, there isn't too much of this initial code to type.You can, if you prefer, modify one of these JavaFX templates, or copy and pasteof the following code. (In NetBeans you would choose new.)Here is the initial code for our class:The significant new code is this:The UI design and construction is provided by a separate FXML file. This file needs to be at the same level as our class, as this is wherewill look.In NetBeans you can right-click on our package folder, choose New, Other, JavaFX and Empty FXML. I don't have such an option in my current version of IntelliJ IDEA, although I feel sure it could be added. I just right-clicked and chose New, File and made sure I typed the full filename, including the fxml extension (). IntelliJ recognises this file-type and provides colour-coding and code-assistance.

wiki said:

FXML is a declarative XML-based language created by Oracle Corporation for defining the user interface of a JavaFX 2.0 application.

<tag attribute="value" attribute="value">tag content (text)</tag>

<tag attribute="value" />

Spoiler

IntelliJ would have created this simplified FXML code:



Spoiler

<?import javafx.geometry.Insets?> <?import javafx.scene.layout.GridPane?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <GridPane fx:controller="fxapplication2.Controller" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10"> </GridPane>



My current version of NetBeans creates a full sample-application, so would create this fuller code:



Spoiler

<?xml version="1.0" encoding="UTF-8"?> <?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml" fx:controller="javafxapplication2.Controller"> <children> <Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" /> <Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" /> </children> </AnchorPane>



I think it would useful later to compare these two pieces of code - but not now.

IntelliJ would have created this simplified FXML code:My current version of NetBeans creates a full sample-application, so would create this fuller code:I think it would useful later to compare these two pieces of code - but not now.

<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.layout.GridPane?> <GridPane fx:controller="fxapplication2.Controller" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10"> </GridPane>

<?import javafx.scene.layout.GridPane?>

<GridPane fx:controller="fxapplication2.Controller" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10"> </GridPane>

fx:controller

xmlns:fx

package fxapplication2; public class Controller { }

is matched by a closing .However, some tags don't require a closing tag, so look like this:Here is the FXML code that we will start with:The amount of intelli-sense and auto-completion that your IDE provides for FXML will vary, although it may improve after you've built and run the application once.The first line is the XML declaration, a processing instruction that identifies this document as being XML. Technically, this is required for all XML documents. However, because FXML is specific to Java it can be omitted.This is an import processing instruction (PI) and mirrors what we would do in in a .java file.This specifies our root-node as a GridPane and theattribute associates a "controller" class with our FXML document. (We'll build this in a second.)specifies the xml-namespace for the fx:GridPane. I don't think it will help to describe this in any more detail here; it is not essential knowledge for us and we can move on.Let's create our controller, a new Java classat the same level as our main class: FXML :wikiHow to describe XML (Extensible Markup Language) briefly? It is a markup language, similar to HTML. I suggest that you continue with the tutorial but, if the FXML confuses too much, you might pause to do a little reading-up on this subject. Basically, it consists of these:where each opening.However, some tags don't require a closing tag, so look like this:Here is the FXML code that we will start with:The amount of intelli-sense and auto-completion that your IDE provides for FXML will vary, although it may improve after you've built and run the application once.The first line is the XML declaration, a processing instruction that identifies this document as being XML. Technically, this is required for all XML documents. However, because FXML is specific to Java it can be omitted.This is an import processing instruction (PI) and mirrors what we would do in in a .java file.This specifies our root-node as a GridPane and theattribute associates a "controller" class with our FXML document. (We'll build this in a second.)specifies the xml-namespace for the fx:GridPane. I don't think it will help to describe this in any more detail here; it is not essential knowledge for us and we can move on.Let's create our controller, a new Java classat the same level as our main class:

wiki said:

A controller can send commands to the model to update the model's state (e.g., editing a document). It can also send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document).

Spoiler

I tend to think of the controller as a bridge, a channel of communication, between the model and view(s). That is, passing messages (and data) between them. To me the term controller gives the impression of something sitting above everything else, overseeing and controlling everything that happens - which isn't accurate.



<?import javafx.geometry.Insets?> <GridPane fx:controller="fxapplication2.Controller" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10"> <padding><Insets top="25" right="25" bottom="25" left="25" /></padding> </GridPane>

Spoiler

<?import javafx.scene.text.Text?> <?import javafx.scene.control.*?> <padding><Insets top="25" right="25" bottom="25" left="25" /></padding> <Text text="Welcome" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2" /> <Label text="User Name:" GridPane.columnIndex="0" GridPane.rowIndex="1" /> <TextField GridPane.columnIndex="1" GridPane.rowIndex="1" /> <Label text="Password:" GridPane.columnIndex="0" GridPane.rowIndex="2" /> <PasswordField fx:id="passwordField" GridPane.columnIndex="1" GridPane.rowIndex="2" /> </GridPane>



<?import javafx.scene.layout.HBox?> <HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="1" GridPane.rowIndex="4"> <Button text="Sign In" onAction="#handleSubmitButtonAction" /> </HBox> <Text fx:id="actiontarget" GridPane.columnIndex="1" GridPane.rowIndex="6" />

package fxapplication2; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.text.Text; public class Controller { @FXML private Text actiontarget; @FXML protected void handleSubmitButtonAction(ActionEvent event) { actiontarget.setText("Sign in button pressed"); } }

This controller will eventually contain our event-code. The spoiler contains a personal opinion and could be skipped.You can now run our application although it doesn't yet do very much.We will add the same padding that our first application had:The following additions should require little explanation, especially if you compare them to our first application.Add our Button and Text:Theattribute identifies the method that we will create in our controller to respond to clicking the button.Thevalue on an element creates a variable in the document's namespace, which we can refer to in the controller.Modify the controller:Thebinds the corresponding field in the FXML to the annotated variable. This initially struck me as slightly odd, an act of faith that the variables would have the same name. If you mis-spell it, though, it will create errors when you click the button. (IDEs continue to improve, perhaps they will be able to indicate such errors for us - maybe some already do.)

tutoracles said:

As an alternative to using Java code to create an event handler, you can create the handler with any language that provides a JSR 223-compatible scripting engine. Such languages include Javascript, Groovy, Jython, and Clojure.

Spoiler

<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.layout.GridPane?> <?import javafx.geometry.Insets?> <?import javafx.scene.text.Text?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.HBox?> <GridPane fx:controller="fxapplication2.Controller" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10"> <padding> <Insets top="25" right="25" bottom="25" left="25"/> </padding> <Text text="Welcome" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"/> <Label text="User Name:" GridPane.columnIndex="0" GridPane.rowIndex="1"/> <TextField GridPane.columnIndex="1" GridPane.rowIndex="1"/> <Label text="Password:" GridPane.columnIndex="0" GridPane.rowIndex="2"/> <PasswordField fx:id="passwordField" GridPane.columnIndex="1" GridPane.rowIndex="2"/> <HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="1" GridPane.rowIndex="4"> <Button text="Sign In" onAction="#handleSubmitButtonAction"/> </HBox> <Text fx:id="actiontarget" GridPane.columnIndex="1" GridPane.rowIndex="6"/> </GridPane>



Styling with CSS

Scene scene = new Scene(root, 300, 275); stage.setScene(scene); scene.getStylesheets().add (FXApplication2.class.getResource("Login.css").toExternalForm()); stage.show();

.label { -fx-font-size: 12px; -fx-font-weight: bold; -fx-text-fill: #333333; -fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 ); }

<GridPane fx:controller="fxapplication2.Controller" styleClass="root" xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10"> <padding><Insets top="25" right="25" bottom="25" left="25" /></padding> <Text text="Welcome" id="welcome-text" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2" />

Concluding

This may be an option for you, although you are less likely to receive intelli-sense/completions help in your chosen editor.there are GUI builders that take a visual, drag-and-drop, approach to constructing GUIs, including JavaFX Scene Builder . These would create the FXML for you. I cannot speak from personal experience (I don't use them) but I believe that the quality of the code that these tools generate remains quite poor. Investigate this if it interests you. Even if it does, I urge you to continue with the current text-based approach until you have a solid understanding of FXML.Here is the complete FXML code if you need it:Either of our versions can be styled using Cascading Style Sheets (CSS) but I'll refer you back to the Oracle tutorials for this aspect.- there is a section towards the end that shows how to apply the css to our second application, within the FXML.I just copied the Login.css file and the background.png image and made the following amendments to our second application to get this to work. (This runs, and works, but is missing a number of things that you can learn from the tutoracles.)Here is an example of the css:It uses JavaFX prefixes -fx and a number of implicit class-names.Two slight adjustments to the FXML give a fuller picture of how the css works, adding a styleClass and id:Using an external stylesheet is another example of separation of concerns I hope that this has been helpful. I still suggest that you read through the Oracle tutorials, both to fill in some details that I may have omitted and to discover links to other resources and further tutorials.I also encourage you to use JavaFX with FXML, and the MVC pattern. It is the modern approach. The FXML may be unfamiliar but a good IDE will provide code-assistance for you and, after all, it is just a list of tags and attributes!

This post has been edited by andrewsw: 12 February 2014 - 02:27 AM