JavaFX
Available Literature
The AP Group holds two library books related to JavaFX that can be used as a reference:
JavaFX 8 - Introduction by ExampleJavaFX 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.
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:
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
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 < 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
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.
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) -> {
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.
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) -> {
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");
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.
Upgrading from OpenJFX 13
Here is some documentation about our
process to upgrade from OpenJFX 13.
--
RaphaelMueller - 30 Jan 2015
--
AnnekeWalter - 06 Oct 2016