Page 1 of 1

Swing to JavaFX Rate Topic: ***** 2 Votes

#1 andrewsw  Icon User is offline

  • Fire giant boob nipple gun!
  • member icon

Reputation: 3354
  • View blog
  • Posts: 11,354
  • Joined: 12-December 12

Posted 11 February 2014 - 12:42 PM

*
POPULAR

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:

1 Hello World, JavaFX Style
2 Creating a Form in JavaFX
4 Using FXML to Create a User Interface
3 Fancy Forms with JavaFX CSS

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:

  • 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.

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.

JavaFX - the Swing-like Approach

Create a new, empty, Java project named JavaFX1,
containing a package named fxapplication1 and
a 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:
package fxapplication1;

import javafx.application.Application;
import javafx.stage.Stage;

public class FXApplication1 extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        
    }
}


(Well, it is not quite the minimum because the @Override annotation and throws Exception could be considered optional.)

start() is the entry-point for JavaFX applications. However, depending on how we run and package our application we can, and perhaps should, include a main() method. 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..):
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 {

    }
}


Stage 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: Stage is the primary object in JavaFX.

A Stage requires a Scene. 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 StackPane. Using Built-in Layout Panes: Oracle.

Posted Image

So here is our revised (new and improved) bare-minimum code:
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();
    }
}


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:

Working with the JavaFX Scene Graph

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:
        stage.setTitle("Welcome to JavaFX!");



Spoiler

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:
        stack.getChildren().add(btn);


The use of getChildren() tells 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 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");
            }


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.

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.

Spoiler

Insets 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. getChildren().add() 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:

Spoiler

The imports are becoming unwieldy, let's tidy them a little:

Spoiler

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.

Spoiler


Posted Image

Here is the full, current, code:

Spoiler

This is all quite similar to Swing. The Button is added to an HBox, which has its alignment set to bottom-right before being added to the grid.

Notice that actionTarget is a Text object rather than requiring a Label. This is another (small) indication of the flexibility of JavaFX.

Uncomment the line grid.setGridLinesVisible(true); and run it again. These gridlines are handy but don't leave them visible in the final code!

Posted Image

Posted Image

MVC With FXML

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.

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 named fxapplication2 within our project, and then a new class FXApplication2.




I 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 paste some of the following code. (In NetBeans you would choose new JavaFX FXML Application.)




Here is the initial code for our class:

Spoiler

The significant new code is this:
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;

        Parent root = FXMLLoader.load(getClass().getResource("fxapplication2.fxml"));

        stage.setScene(new Scene(root, 300, 275));


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 where getResource() will 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 (fxapplication2.fxml). 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.

FXML :wiki

Introduction to FXML

How 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:
<tag attribute="value" attribute="value">tag content (text)</tag>


where each opening <tag> is matched by a closing </tag>.

However, some tags don't require a closing tag, so look like this: <tag attribute="value" />.

Spoiler

Here is the FXML code that we will start with:
<?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>


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.
<?import javafx.scene.layout.GridPane?>


This is an import processing instruction (PI) and mirrors what we would do in in a .java file.
<GridPane fx:controller="fxapplication2.Controller"
          xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
</GridPane>


This specifies our root-node as a GridPane and the fx:controller attribute associates a "controller" class with our FXML document. (We'll build this in a second.) xmlns:fx 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 class Controller at the same level as our main class:
package fxapplication2;

public class Controller {
}


Introduction to FXML :Controllers

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).

This controller will eventually contain our event-code. The spoiler contains a personal opinion and could be skipped.

Spoiler

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:
<?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>


The following additions should require little explanation, especially if you compare them to our first application.

Spoiler

Add our Button and Text:

<?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" />


The onAction attribute identifies the method that we will create in our controller to respond to clicking the button.

The fx:id value on an element creates a variable in the document's namespace, which we can refer to in the controller.

Modify the controller:
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");
    }
}


The @FXML annotation binds 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.

This may be an option for you, although you are less likely to receive intelli-sense/completions help in your chosen editor.

Note that 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:

Spoiler

Styling with CSS

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.

3 Fancy Forms with JavaFX CSS

4 Using FXML to Create a User Interface
- 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.)
        Scene scene = new Scene(root, 300, 275);
        stage.setScene(scene);
        scene.getStylesheets().add
                (FXApplication2.class.getResource("Login.css").toExternalForm());

        stage.show();


Posted Image

Here is an example of the css:
.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 );
}


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:
<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" />


Posted Image

Using an external stylesheet is another example of separation of concerns.

Concluding

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


Is This A Good Question/Topic? 5
  • +

Page 1 of 1