JavaFX

Available Literature

The AP Group holds two library books related to JavaFX that can be used as a reference:

JavaFX 8 - Introduction by Example
JavaFX 8 - Pro

The following information is an excerpt from these books to provide some basic knowledge. Please consult the books for further and more detailed information on the topics.

Another resource of course is the documentation by Oracle: http://docs.oracle.com/javase/8/javase-clienttechnologies.htm

Tutorial

Start presentation

Slide 1: Introduction

JavaFX is the graphical user interface (GUI) toolkit of Java 8. Since it is built from the ground up it takes advantage of modern GPUs, provides well designed programming interfaces, and is written as a pure Java language API. Some Buzzwords: Modern API, skinnable UI, charting, media support, gestures, touch, 3D graphics, rich text support, embedded support.

We will have a look at:

- Basic JavaFX Concepts
- Drawing with JavaFX (programmatically)
- Control elements
- FXML (defining a GUI via XML style syntax)
- Styling a GUI
- Replacement for property change support (properties and bindings)
- Observables

Command Line Hello World

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class FXDemo extends Application {

        @Override
        public void start(Stage stage) {
                Text text = new Text("Hello World!");
                text.setY(text.getFont().getSize());
                Scene scene = new Scene(new Group(text));

                stage.setTitle("Welcome to JavaFX!");
                stage.setScene(scene);
                stage.sizeToScene();
                stage.show();
        }

        public static void main(String[] args) {
                launch(args);
        }

}

Scene Graph

More information: Working with the JavaFX Scene Graph

The individual items held within the JavaFX scene graph are known as nodes. Each node is classified as either a branch node (meaning that it can have children), or a leaf node (meaning that it cannot have children). The first node in the tree is always called the root node, and it never has a parent.

sceneGraphFigure1.png

The JavaFX API defines a number of classes that can act as root, branch or leaf nodes. When substituted with actual class names, this same figure might resemble that shown here:

sceneGraphFigure2.png

Node

http://docs.oracle.com/javase/8/javafx/api/javafx/scene/Node.html

Each item in the scene graph is called a Node. Branch nodes are of type Parent , whose concrete subclasses are Group , Region , and Control , or subclasses thereof.

Leaf nodes are classes such as Rectangle , Text , ImageView , MediaView , or other such leaf classes which cannot have children. Only a single node within each scene graph tree will have no parent, which is referred to as the "root" node.

There may be several trees in the scene graph. Some trees may be part of a Scene , in which case they are eligible to be displayed. Other trees might not be part of any Scene .

A node may occur at most once anywhere in the scene graph. Specifically, a node must appear no more than once in all of the following: as the root node of a Scene , the children ObservableList of a Parent , or as the clip of a Node .

The scene graph must not have cycles. A cycle would exist if a node is an ancestor of itself in the tree, considering the Group content ObservableList, Parent children ObservableList, and Node clip relationships mentioned above.

If a program adds a child node to a Parent (including Group, Region, etc) and that node is already a child of a different Parent or the root of a Scene, the node is automatically (and silently) removed from its former parent. If a program attempts to modify the scene graph in any other way that violates the above rules, an exception is thrown, the modification attempt is ignored and the scene graph is restored to its previous state.

Setting up Eclipse

e(fx)clipse plugin

e fx clipsePluginInstall.png

e fx clipsePluginSceneBuilder.png
Maven and JavaFX

Archetype for FX Programs at GSI (test phase)
<groupId>de.gsi.cs.co<groupId>
<artifactId>csco-java-fx-template<artifactId>

The archetype uses our usual "csco-parent-java-bundle". So far all the packaging seems to work fine.

There is also a maven plugin dedicated to build JavaFX applications for different platforms and has also support for packaging native installers/executables:
Read more here: javafx-maven-plugin bei GitHub
<dependency>
  <groupId>com.zenjava</groupId>
  <artifactId>javafx-maven-plugin</artifactId>
  <version>8.1.2</version>
</dependency>

So far it seems not to be necessary to use this plugin to deploy a JavaFX application in the CSCO environment.

Slide 2: Programmatic approach to basic shapes and colors

// We can modify the trees of the scene after it is shown
      // Lets draw a line and add it to a group
      Line line = new Line(10, 10, 50, 50);
      line.setStroke(Color.RED);
      group.getChildren().add(line);

Slide 3: Layout and UI Controls

Layouts

The JavaFX API has a stock of layouts that provide the most common ways to display UI controls. They help to provide a pleasent user experience in GUI applications to allow the user to resize a viewable area while UI controls also resize.

The most common Layouts are
  • HBox - Horizontal layout of children.
  • VBox - Vertical layout of children.
  • FlowPane - Nodes flow horizontal and wrap to the next line if not enough space is available.
  • BorderPane - Allows child nodes to be places top, bottom, left, right or centered.
  • GridPane - Allows to align children in a grid, constraints can be specified on a row, column or cell level.

@Override
   public void start(Stage stage) {
      HBox hBox = new HBox();
      hBox.setBackground(new Background(new BackgroundFill (Color.BEIGE, null, null)));
      hBox.getChildren().add(new Label("hBox"));

      VBox vBox = new VBox();
      vBox.setBackground(new Background(new BackgroundFill (Color.RED, null, null)));
      vBox.getChildren().add(new Label("vBox1"));
      vBox.getChildren().add(new Label("vBox2"));
      vBox.getChildren().add(new Label("vBox3"));
      hBox.getChildren().add(vBox);

      FlowPane flowPane = new FlowPane ();
      flowPane.setBackground(new Background(new BackgroundFill (Color.GREEN, null, null)));
      for (int i = 1; i &lt; 20; i ++) {
         flowPane.getChildren().add(new Label(" flowPane" + i));
      }
      hBox.getChildren().add(flowPane);

      BorderPane borderPane = new BorderPane ();
      borderPane.setBackground(new Background(new BackgroundFill (Color.BLUE, null, null)));
      borderPane.getChildren().add(new Label("borderPane"));
      hBox.getChildren().add(borderPane);

      GridPane gridPane = new GridPane ();
      gridPane.setBackground(new Background(new BackgroundFill (Color.YELLOW, null, null)));
      gridPane.getChildren().add(new Label("gridPane"));
      hBox.getChildren().add(gridPane);

      Scene scene = new Scene(hBox, 800, 600);   
      stage.setTitle("Welcome to JavaFX!");
      stage.setScene(scene);
      stage.sizeToScene();
      stage.centerOnScreen();
      stage.show();
   }

Margins, Border, Padding, Spacing

marginPaddingSpacing.png

UI Controls

UI Control nodes and shape nodes are treated the same by JavaFX, they can coexist on the scene graph and can be scaled, rotated, styled and effects can be added.

uiControls.png

final static class Counter {
      private static int buttonPressedCount = 0;

      public static int buttonPressed() {
         buttonPressedCount++;
         return buttonPressedCount;
      }
   }

   @Override
   public void start(Stage stage) {
      HBox hBox = new HBox();
      hBox.setBackground(new Background(new BackgroundFill (Color.BEIGE, null,
            null)));
      hBox.setAlignment(Pos.CENTER);

      Button button = new Button("Press me!");
      button.setTextAlignment(TextAlignment.CENTER);
      button.setRotate(-45);

      Label label = new Label("Button pressed 0 times");
      label.setTextAlignment(TextAlignment.CENTER);
      label.setScaleX(2);
      label.setScaleY(2);
      label.setRotate(45);

      button.setOnAction((event) -&gt; {
         label.setText("Button pressed " + Counter.buttonPressed() + " times");
      });

      hBox.getChildren().add(button);
      hBox.getChildren().add(label);

      Scene scene = new Scene(hBox, 800, 600);
      stage.setTitle("Welcome to JavaFX!");
      stage.setScene(scene);
      stage.sizeToScene();
      stage.centerOnScreen();
      stage.show();
   }

Slide 4: SceneBuilder

You are familiar with the theater metaphor of the JavaFX UI, with the Stage representing a window, the Scene and the Nodes representing the content of the UI. Instead of coding and styline the UI programmatically it is also possible with JavaFX to develop a UI graphically and create UIs declarative.

javaFxSceneBuilder.png

The SceneBuilder provides a 'Library' with Items you can utilize in you UI. A 'Document' panel shows the SceneGraph and information about the Controller Class used. As well as an Inspector that gives you details on the selected Component like Properties, Layout constraints and Code invocations.

FXML

At the center of the approach the SceneBuilder saves UI projects as a single file that is similar to XML, a so called FXML file. It contains the 'what' of the UI elements, nut not the 'how'. This is why this method is called declarative. A simple example of an FXML file:

<!--?xml version="1.0" encoding="UTF-8"?-->

<!--?import java.lang.*?-->
<!--?import javafx.scene.control.*?-->
<!--?import javafx.scene.layout.*?-->
<!--?import javafx.scene.layout.AnchorPane?-->

<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="de.gsi.cs.co.TestController">
   <children>
      <Button fx:id="idOfMyButton" mnemonicParsing="false" onAction="#buttonActionHappened"  text="Button " />
   </children>
</AnchorPane>

This file holds an AnchorPane as the top most element. The AnchorPane has a Button as its only child. The 'import' instructions at the top of the file inform the consumer which classes need to be available on the class path. The 'fx:controller' attribute defines the Java Class that holds the application logic. The 'fx:id' attribute allows us to assign an identifier to an element so we can later access it in the Java code. Attributes that are not starting with 'fx' or are namespace attributes 'xmlns' are usually properties that are set, like e.g. the 'text' of the button.

Controller

The following class was specified in the FXML as the 'fx:controller' class.

package de.gsi.cs.co;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class TestController {

    @FXML private ResourceBundle resources;
    @FXML private URL location;
    @FXML private Button idOfMyButton;

    @FXML
    void buttonActionHappened(ActionEvent event) {
        // Event handling happens here
    }

    @FXML
    void initialize() {
        // Initialization happens here
        assert idOfMyButton != null : "fx:id=\"idOfMyButton\" was not injected: check your FXML file.";
    }
}

It must have fields that match the fx:ids that we used in the FXML file, as well as methods and signature that match the ones we specified as event handlers in the FXML. JavaFX needs to now which fields and methods should be used. If the @FXML annotation is applied to a field it tells JavaFX that this field can be used as an fx:id. If it is applied to a method this method can be used as an event handler. Properly annotated fields and methods are also shown in the SceneBuilder, this way the controller can be 'wired' with the UI.

Loader

The FXMLLoader is responsible for reading the FXML file, parsing it and instantiating all the nodes specified in the FXML file. It also 'wires' the objects according to the relationships specified in the FXML file.
public void start(Stage stage) throws Exception {
        // Load the FXML (instantiates also the controller)
        String fxmlFile = "/de/gsi/cs/co/test.fxml";
        FXMLLoader loader = new FXMLLoader();
        Parent rootNode = (Parent) loader.load(getClass().getResourceAsStream(fxmlFile));
        TestController testController = loader.getController();
        // Add the FXML nodes to a scene and show them
        Scene scene = new Scene(rootNode);
        stage.setScene(scene);
        stage.show();
    }
Since we specified a controller in the FXML file the load also creates an instance of TestController and assignes the nodes to the fields according to the fx:id that we specified. This is called 'injecting' the nodes into the controller and also hooks up the event handler methods (for a short introduction look at http://en.wikipedia.org/wiki/Dependency_injection ).

JavaFX CSS (Cascaded Style Sheets, Theming)

A term we already used is 'look and feel' in the context of UIs. In JavaFX you have the ability to create, modify, or use existing themes to 'skin' your applications. This basically will change how UI Controls look. Some complete sets of skins (what we before called look and feel) are also available in the JavaFX distribution.

Here is an example (testStyles.css) how the default background color and the background when the mouse is hovering over the button are changed.
.button {
    -fx-background-color: #DD0000;
}
.button:hover{
    -fx-background-color: #FF0000;
}

// ...
        // creating the (root)Nodes was omitted here
        // ...
        Scene scene = new Scene(rootNode);
        scene.getStylesheets().add("/de/gsi/cs/co/testStyles.css");
        stage.setScene(scene);
        stage.show();
'getStylesheets().add(...)' only applies a stylesheet to one Scene. If you want that a stylesheet is applied by default to all scenes owned by an application you have to use 'Appliation.setUserAgentStylesheet(...)' instead.

A detailed information which style attributes are available can be accessed at the Oracle site: http://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html

Slide 5: Properties

Properties are basically wrapper objects around attributes like String or Integer. The provide the possibility to add listener code to respond when the wrapped object changes or is flagged as invalid.For synchronization purpose properties can be bound to one another.

Properties

There are two basic types of JavaFX properties

- Read/Writable

- Read-Only

Properties are usually named like the Object they wrap. For example SimpleBooleanProperty and =ReadOnlyBooleanWrapper.
// Read/Write Property
StringProperty password = new SimpleStringProperty ("initialpassword");
password.set("newpassword");
System.out.println("Current password is " + password.get() );

// Readonly Property
ReadOnlyStringWrapper userName = new ReadOnlyStringWrapper ("sheldon");
ReadOnlyProperty readOnlyUserName = userName.getReadOnlyProperty();

JavaFX Bean

Since JavaFX Beans are Property based, in addition to the usual getXXX(..) and setXXX(...) methods a JavaFX bean also provides methods like XXXProperty().
private StringProperty password; // Initialize this in the constructor

public final String getPassword() {
   return password.get();
}
public final void setPassword(String password) {
   this.password.set(password)
}
public StringProperty passwordProperty() {
   return password;
}

Property Change Support

JavaFX property objects provide an addListener(...) method. This enables the programmer to add handler code that will respond when a property changes. The method will accept two functional interfaces, ChangeListener and InvalidationListener.
private StringProperty example; // Initialize this in the constructor

// Anonymous class
example.addListener(new ChangeListener () {
   @Override
   public void changed(ObservableValue o, Object oldVal, Object newVal) {
      System.out.println("example has changed!");
   }
});

// Lambda
example.addListener( (observableValue, oldValue, newValue) -&gt; {
      System.out.println("example has changed!");
});

Slide 6: Binding

Binding synchronizes values (properties). That means if one (dependent) variable changes, the other variable changes. To bind one property to another one will invoke the bind() method.

Bidirectional binding

Bind properties with the same type. A change to one property will also propagate to the other.

public final static class Contact {

      private static StringProperty firstName = new SimpleStringProperty ();
      private static StringProperty lastName = new SimpleStringProperty ();

      public final static StringProperty firstNameProperty() {
         return firstName;
      }
      public final static StringProperty lastNameProperty() {
         return lastName;
      }
   }

   public final static void main(String[] args) {
      // We bind the name of "Contact" with a local variable
      SimpleStringProperty localName = new SimpleStringProperty ();
      localName.bindBidirectional(Contact.firstNameProperty());
      // We change the local variable
      localName.set("Raphael");
      // and the bound variable of "Contact" changed
      System.out.println(Contact.firstNameProperty());
   }

High-level binding

A fluent interface (chainable, intuitive names) to properties to bind a value to performed operations.

// Area = width * height
IntegerProperty width = new SimpleIntegerProperty (2);
IntegerProperty height= new SimpleIntegerProperty (5);

NumberBinding area = width.multiply(height);

Low-Level binding

Extends a "Binding" class and overrides computeValue() to formulate complex operations.

DoubleProperty radius = new SimpleDoubleProperty (2);
DoubleBinding volumeOfSphere = new DoubleBinding () {
  {
    // Bind double to radius
    super.bind(radius);
  }

  @Override
  protected double computeValue() {
    return (4/3 * Math.PI * Math.pow(radius.get()), 3));
  }
};

Observables (Collections)

// Inject observable into ListView
            ListView<String> listView = new ListView<String>();
            ObservableList<String> observableContactList = FXCollections.observableArrayList();
            listView.setItems(observableContactList);

            // Automatically updates UI
            observableContactList.add("Raphael");

Slide 7: Mixing with other UI Toolkits

Be aware that usually other UI toolkits have their own worker threads. If you integrate them with JavaFX you have to care about concurrency and multi-threading issues!

JavaFX in Swing

// when the first JFXPanel object is created, it implicitly initializes the JavaFX runtime.
      JFXPanel jfxPanel = new JFXPanel();
      jfxPanel.setScene(scene);

      // Create a swing frame and add the panel
      JFrame jFrame = new JFrame();
      jFrame.add(jfxPanel);
      jFrame.pack();
      jFrame.setVisible(true);

Swing in JavaFX

// Create a SwingNode with a Button as content
      SwingNode swingNode = new SwingNode ();
      swingNode.setContent(new JButton("Click me!"));
      // Add it to a scenen and display it
      Scene scene = new Scene(new AnchorPane (swingNode), 800, 600);
      stage.setScene(scene);
      stage.sizeToScene();
      stage.centerOnScreen();
      stage.show();

JavaFX 8 Tests and Restrictions

JavaFX will make use of hardware acceleration and thus depends more on an appropriate setup of graphics and sound card drivers as well as the appropriate codecs (libav, directshow).
See sections 'JavaFX Graphics Support' and 'JavaFX Media' in Oracle JDK 8 and JRE 8 Certified System Configurations Contents .


Test so far have shown that JavaFX 3D will not be possible using X Remoting / XDMCP. 3D will simply not be activated.
Also 2D acceleration does not take place and is replaced by software rendering, this will have an heavy impact on the peformance.

For the future consoles this ight not be a problem since the plans are to run the software locally on the console, but if 3D is necessary and will be used we have to think about concepts for development.

Example of some JavaFX (3D) command line options (for debugging purpose):
/usr/java/jdk8/bin/java -Dprism.verbose=true -Dprism.order=es2,sw,j2d -Dprism.forceGPU=true -jar Ensemble8.jar

Findings of JavaFX tests by Anne, Tips etc.

Known Bugs

Here is a list of known bugs.

-- RaphaelMueller - 30 Jan 2015

-- AnnekeWalter - 06 Oct 2016

Topic attachments
I Attachment Action Size Date Who Comment
areaLayout.svgsvg areaLayout.svg manage 6 MB 18 Nov 2016 - 12:20 RaphaelMueller ZKS Area Overview
e_fx_clipsePluginInstall.pngpng e_fx_clipsePluginInstall.png manage 106 K 09 Feb 2015 - 12:01 RaphaelMueller  
e_fx_clipsePluginSceneBuilder.pngpng e_fx_clipsePluginSceneBuilder.png manage 38 K 09 Feb 2015 - 12:03 RaphaelMueller  
javaFxSceneBuilder.pngpng javaFxSceneBuilder.png manage 51 K 13 Feb 2015 - 13:06 RaphaelMueller  
marginPaddingSpacing.pngpng marginPaddingSpacing.png manage 373 K 10 Feb 2015 - 10:57 RaphaelMueller  
sceneGraphFigure1.pngpng sceneGraphFigure1.png manage 11 K 09 Feb 2015 - 11:20 RaphaelMueller  
sceneGraphFigure2.pngpng sceneGraphFigure2.png manage 11 K 09 Feb 2015 - 11:20 RaphaelMueller  
uiControls.pngpng uiControls.png manage 20 K 13 Feb 2015 - 12:48 RaphaelMueller  
Topic revision: r26 - 02 Jun 2020, BenjaminPeter
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback