JavaFX - baresi.faculty.polimi.it · –A JavaFX application can have multiple windows open –Each...

Preview:

Citation preview

JavaFX

Java and JavaFX

• You must have a new version of Java• And install JavaFX

– Stable JavaFX11 but also JavaFX14– https://openjfx.io/openjfx-docs/#install-java– https://gluonhq.com/products/javafx/

• Removed from JDK 11– JavaFX 11 is now a standalone module– Developers must now explicitly include JavaFX modules

in applications

JavaFX

• JavaFX is somewhat inspired by Flash/Flex• Provides a client application platform for desktop,

mobile, and embedded systems• Is a runtime available as platform-specific SDK, as

jmod files, and as a set of Maven artifacts

Main modules

Architecture

JavaFX Public API’s and Scene Graph

Quantum Toolkit

PrismGlass Windowing

ToolkitMedia Engine

Web EngineJava

2DOpen

GL D3D

Java Virtual Machine

Structure

Scene graph

• Directed acyclic graph • Parent and child • Representation of the GUI component

11 | © 2011 Oracle Corporation – Proprietary and Confidential

Scene Graph

● Directed acyclic graph ● Parent and child ● Representation of the GUI component

Main concepts

• The stage is the outer frame for a JavaFX application– A JavaFX application can have multiple windows open– Each window has its own stage

• To display anything on a stage in a JavaFX application, you need a scene– A stage can only show one scene at a time, but it is possible

to exchange the scene at runtime

• All visual components (controls, layouts etc.) must be attached to a scene to be displayed– The scene must be attached to a stage for the whole scene

to be visible– The total object graph of all the controls, layouts etc.

attached to a scene is called the scene graph

Nodes

• All components attached to the scene graph are called nodes– Branch nodes can contain other nodes– Leaf nodes cannot contain other nodes

• Controls are components that provide some kind of control functionality– For a control to be visible it must be attached to

a Scene object

• Controls are usually nested inside a layout component that manages the layout of controls relative to each other

Layouts

• JavaFX layouts are components that contain other components inside them and manage their layout– A layout component must be attached to the scene

graph of a Scene object– It is possible to nest layout components inside other

layout components

Other elements

• Charts• 2D Graphics• 3D Graphics• Audio• Video• WebView

Node

Parent

Control

Shapes

Just a Stage

public class Main extends Application {@Overridepublic void start(Stage primaryStage) throws Exception {

primaryStage.setTitle("My First JavaFX App");primaryStage.show();

}

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

}}

… and Scene

public class Main extends Application {@Overridepublic void start(Stage primaryStage) throws Exception {

primaryStage.setTitle("My First JavaFX App");

Label label = new Label("Hello World, JavaFX !");Scene scene = new Scene(label, 400, 200);primaryStage.setScene(scene); primaryStage.show();

}

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

}}

… and another Stage

public class Main extends Application {public static void main(String[] args) {

launch(args);}

@Overridepublic void start(Stage primaryStage) {

primaryStage.setTitle("JavaFX App");

Stage stage = new Stage();stage.initModality(Modality.APPLICATION_MODAL);//stage.initModality(Modality.WINDOW_MODAL);//stage.initModality(Modality.NONE);

primaryStage.show();stage.showAndWait();

}}

Stage Style

• Decorated: a standard window with OS decorations (title bar and minimize / maximize / close buttons), and a white background

• Undecorated: a standard window without OS decorations, but still with a white background

• Transparent: an undecorated window with transparent background

• Unified: like a decorated stage, with no border between the decoration area and the main content

• Utility: a decorated window, but with minimal decorations

stage.initStyle(StageStyle.DECORATED);

2D elements

public class Main extends Application {@Overridepublic void start(Stage stage) {

Group root = new Group();Scene scene = new Scene(root,100,100);stage.setScene(scene);

Circle c1 = new Circle(50.0f, 50.0f, 40.0f, Color.RED);

root.getChildren().add(c1);stage.show();

}

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

} }

ImageViewpublic class Main extends Application {

@Overridepublic void start(Stage primaryStage) throws Exception {

primaryStage.setTitle("ImageView Experiment 1");

FileInputStream input = new FileInputStream("…/mario.jpg");Image image = new Image(input);ImageView imageView = new ImageView(image);

HBox hbox = new HBox(imageView);Scene scene = new Scene(hbox, 220, 300);primaryStage.setScene(scene);primaryStage.show();

}

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

}}

Frills

• Margin• Node Alignment

– Pos.BASELINE_LEFT/Pos.BASELINE_CENTER/Pos.BASELINE_RIGHT/Pos.BOTTOM_LEFT/Pos.BOTTOM_CENTER/Pos.BOTTOM_RIGHT/Pos.CENTER_LEFT/Pos.CENTER/Pos.CENTER_RIGHT/Pos.TOP_LEFT/Pos.TOP_CENTER/Pos.TOP_RIGHT/

• Hgrow– To define how a child node can grow horizontally to fill

any space available inside the Hbox– Priority.ALWAYS/ Priority.SOMETIMES/ Priority.NEVER

• FillHeight– To tell the HBox control whether it should expand the

height of its children to fill out the whole height of the Hbox

– True/False

public class Main3 extends Application {@Overridepublic void start(Stage primaryStage) throws Exception {primaryStage.setTitle("HBox Experiment");

Button button1 = new Button("Button 1");

HBox hbox = new HBox(button1);button1.setMaxHeight(99999.0D);HBox.setMargin(button1, new Insets(10, 10, 10, 10));HBox.setHgrow(button1, Priority.ALWAYS);

hbox.setAlignment(Pos.CENTER);hbox.setFillHeight(true);

Scene scene = new Scene(hbox, 200, 100);primaryStage.setScene(scene);primaryStage.show();

}

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

}}

Button

public void start(Stage primaryStage) throws Exception {primaryStage.setTitle("HBox Experiment 1");

Label label = new Label("Not clicked");Button button = new Button("Click");

button.setOnAction(value -> {label.setText("Clicked!");

});

HBox hbox = new HBox(button, label);

Scene scene = new Scene(hbox, 200, 100);primaryStage.setScene(scene);primaryStage.show();

}

Styled buttons@Overridepublic void start(Stage primaryStage) throws Exception {

primaryStage.setTitle("Button Experiment 1");

Button button1 = new Button("Button 1");Button button2 = new Button("Button 2");Button button3 = new Button("Button 3");Button button4 = new Button("Button 4");

button1.setStyle("-fx-border-color: #ff0000; -fx-border-width: 5px;");button2.setStyle("-fx-background-color: #00ff00");button3.setStyle("-fx-font-size: 2em; ");button4.setStyle("-fx-text-fill: #0000ff");

HBox hbox = new HBox(button1, button2, button3, button4);

Scene scene = new Scene(hbox, 400, 100);primaryStage.setScene(scene);primaryStage.show();

}

ListViewpublic class Main extends Application {

@Overridepublic void start(Stage primaryStage) throws Exception {

primaryStage.setTitle("ListView Experiment 1");

ListView listView = new ListView();listView.getItems().add("Item 1");listView.getItems().add("Item 2");listView.getItems().add("Item 3");

Button button = new Button("Read Selected Value");

button.setOnAction(event -> {ObservableList selectedIndices =

listView.getSelectionModel().getSelectedIndices();

for(Object o : selectedIndices){System.out.println("o = " + o + " (" + o.getClass() + ")");

}});

VBox vBox = new VBox(listView, button);

Scene scene = new Scene(vBox, 300, 120);primaryStage.setScene(scene);primaryStage.show();

}

TextField

public void start(Stage primaryStage) throws Exception {primaryStage.setTitle("HBox Experiment 1");

TextField textField = new TextField();Button button = new Button("Click to get text");

button.setOnAction(action -> {System.out.println(textField.getText());

});

HBox hbox = new HBox(textField, button);

Scene scene = new Scene(hbox, 200, 100);primaryStage.setScene(scene);primaryStage.show();

}

WebView

• Pay attention to including the proper modules

public void start(Stage primaryStage) {primaryStage.setTitle("JavaFX WebView Example");

WebView webView = new WebView();webView.getEngine().load("http://google.com");

VBox vBox = new VBox(webView);Scene scene = new Scene(vBox, 960, 600);

primaryStage.setScene(scene);primaryStage.show();

}

Media

public void start(Stage primaryStage) {final String name = "file:////.../video.mp4";

primaryStage.setTitle("Embedded Media Player");Group root = new Group();Scene scene = new Scene(root, 540, 210);

Media media = new Media(name);MediaPlayer mediaPlayer = new MediaPlayer(media);mediaPlayer.setAutoPlay(true);

MediaView mediaView = new MediaView(mediaPlayer);((Group)scene.getRoot()).getChildren().add(mediaView);

primaryStage.setScene(scene);primaryStage.show();

}

FXML

• JavaFX FXML is an XML format– GUIs similar to web GUIs in HTML or in Android– Allows you to separate your JavaFX layout code from

the rest of your application code

• Can be used to compose the layout of– a whole application GUI– just part of an application GUI

• CSS styling

FXML

<FXML />

Controller.java

public class Controller {…}

FXApplication.java

public class FXApplicationextends Application {…}

Java API and FXML

• Java API for JavaFX– End-to-end Java development– Leverage Java language features– Fluent style API for UI construction– Alternative JVM languages support (eg Groovy, Scala)

with JavaFX– Leverage sophisticated IDEs, deduggers and profiles

• FXML – Scriptable, XML based language for defining UI – Convenient alternative to developing UI

programmatically – Easy to learn and intuitive for developers familiar with

web or markup-based UI technologies

First example

<?xml version="1.0" encoding="UTF-8"?><?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?>

<VBox><children>

<Label text="Hello world FXML"/></children>

</VBox>

How to load an FXML file

@Overridepublic void start(Stage primaryStage) throws Exception {

VBox root = (VBox) FXMLLoader.load(getClass().getResource("example.fxml"));

Scene scene = new Scene(root);primaryStage.setScene(scene);primaryStage.setTitle("A simple FXML Example");primaryStage.show();

}

Properties in FXML

<?xml version="1.0" encoding="UTF-8"?><?import javafx.scene.layout.VBox?><?import javafx.scene.control.Label?>

<VBox spacing="20"><children>

<Label text="Line 1"/><Label text="Line 2"/>

</children></VBox>

Event handlers

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

<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Button?><?import javafx.scene.control.Label?>

<VBox xmlns:fx="http://javafx.com/fxml"><Label fx:id="label1" text="Button not clicked"/><Button fx:id="button1" text="Click me!" onAction="reactToClick()"/>

<fx:script>function reactToClick() {

label1.setText("Button clicked");}

</fx:script></VBox>

Controller Classes

• An FXML controller binds the GUI components declared in the FXML file together– The controller object acts as mediator

• There are two ways to set a controller– We can specify it inside the FXML file– We can set an instance of the controller class on

the FXMLLoader instance used to load the FXML document

Controllers

• Are instantiated by the FXML loader– method initialize() called after loading of the FXML

document– initialize() takes no arguments and returns void

• Must have a public no-args constructor– If it does not exist, an exception at load time is thrown

• Can have accessible methods (event handlers)• The FXML loader will automatically look for

accessible instance variables of the controller– If the name matches the fx:id attribute of an element,

the object reference from FXML is automatically copied into the controller instance variable

Example (I)

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

<?import javafx.scene.layout.VBox?><?import javafx.scene.control.Button?><?import javafx.scene.control.Label?>

<VBox xmlns:fx="http://javafx.com/fxml" spacing="20"><children>

<Label fx:id="label1" text="Line 1"/><Label fx:id="label2" text="Line 2"/><Button fx:id="button1" text="Click me!"

onAction="#buttonClicked"/></children>

</VBox>

Example (II)

import javafx.event.Event;import javafx.fxml.FXML;

public class MyFXMLController {

@FXMLpublic void buttonClicked(Event e){

System.out.println("Button clicked");}

}

Example (III)public void start(Stage primaryStage) throws Exception {

MyFXMLController controller = new MyFXMLController();FXMLLoader loader = new FXMLLoader();loader.setController(controller);

String fxmlDocPath = "…/example.fxml";FileInputStream fxmlStream = new FileInputStream(fxmlDocPath);

VBox root = (VBox) loader.load(fxmlStream);Scene scene = new Scene(root);primaryStage.setScene(scene);primaryStage.setTitle("A simple FXML Example");primaryStage.show();

}

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

<?import javafx.scene.control.*?><?import javafx.scene.layout.*?>

<VBox fx:id="vbox" layoutX="10.0" layoutY="10.0" prefHeight="250.0"prefWidth="300.0" spacing="10" xmlns:fx="http://javafx.com/fxml/1"xmlns="http://javafx.com/javafx/2.2"><style>-fx-padding: 10;-fx-border-style: solid inside;-fx-border-width: 2;-fx-border-insets: 5;-fx-border-radius: 5;-fx-border-color: blue;

</style><children><Label fx:id="inputLbl" alignment="CENTER_LEFT" cache="true"

cacheHint="SCALE" prefHeight="30.0" prefWidth="200.0"text="Please insert Your Input here:" textAlignment="LEFT" />

<TextField fx:id="inputText" prefWidth="100.0" /><Button fx:id="okBtn" alignment="CENTER_RIGHT" contentDisplay="CENTER"

mnemonicParsing="false" text="OK" textAlignment="CENTER" /><Label fx:id="outputLbl" alignment="CENTER_LEFT" cache="true"

cacheHint="SCALE" prefHeight="30.0" prefWidth="200.0" text="Your Input:"textAlignment="LEFT" />

<TextArea fx:id="outputText" prefHeight="100.0" prefWidth="200.0"wrapText="true" />

</children></VBox>

public class Main extends Application {public static void main(String[] args) {

Application.launch(args);}

@Overridepublic void start(Stage stage) throws IOException {

FXMLLoader loader = new FXMLLoader();

String fxmlDocPath = "/Users/luciano/data/example.fxml";FileInputStream fxmlStream = new FileInputStream(fxmlDocPath);

VBox root = (VBox) loader.load(fxmlStream);

Scene scene = new Scene(root);stage.setScene(scene);stage.setTitle("A simple FXML Example");stage.show();

}}

Button

<Button fx:id="okBtn"alignment="CENTER_RIGHT"contentDisplay="CENTER"mnemonicParsing="false"onAction="#printOutput"text="OK"textAlignment="CENTER"

/>

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

<?import javafx.scene.control.*?><?import javafx.scene.layout.*?>

<VBox …<fx:script>

function printOutput() {outputText.setText(inputText.getText());

}</fx:script>

</children></VBox>

New Controllerpublic class MyController {

@FXMLprivate TextField inputText;

@FXMLprivate TextArea outputText;

@FXMLprivate URL location;

@FXMLprivate ResourceBundle resources;

public MyController() {}

@FXMLprivate void initialize() {}

@FXMLprivate void printOutput() {

outputText.setText(inputText.getText());}

}

Method start()

…MyController controller = new MyController();FXMLLoader loader = new FXMLLoader();loader.setController(controller);String fxmlDocPath = "…/example.fxml";FileInputStream fxmlStream = new FileInputStream(fxmlDocPath);…

How can we get this?

public void start(Stage primaryStage) {primaryStage.setTitle("JavaFX Welcome!");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>() {@Overridepublic void handle(ActionEvent e) {

actiontarget.setFill(Color.FIREBRICK);actiontarget.setId("actiontarget");actiontarget.setText("Sign in button pressed");

}});Scene scene = new Scene(grid, 300, 275);primaryStage.setScene(scene); primaryStage.show();

}

Screen builder

https://gluonhq.com/products/scene-builder/

ColorfulCircles

Concurrency

• The graphical user interface is not thread-safe– It can only be accessed and modified from the UI thread

also known as the JavaFX Application thread– Implementing long-running tasks on the JavaFX

Application thread inevitably makes an application UI unresponsive

• A best practice is to do these tasks on one or more background threads– and let the JavaFX Application thread process user

events

Communication

• JavaFX provides javafx.concurrent to take care of multithreaded code that interacts with the UI– and ensures that this interaction happens on the correct

thread– The package leverages the existing API by considering

the JavaFX Application thread and other constraints faced by GUI developers

Main elements

• Interface Worker defines an object that performs some work on one or more background threads– The state of the Worker object is observable and usable

from the JavaFX Application thread

• Class Task enables developers to implement asynchronous tasks in JavaFX applications

• Class Service executes tasks

import javafx.concurrent.Task;

public class Fibonacci extends Task<Long> {private final int n;

public Fibonacci(int n) {this.n = n;

}

@Overrideprotected Long call() throws Exception {

updateMessage("Processing");long result = fibonacci(n);updateMessage("Done");return result;

}

public long fibonacci(long number) {if (number == 0 || number == 1)

return number;else

return fibonacci(number - 1) + fibonacci(number - 2);}

}

btnStart.setOnAction(new EventHandler<ActionEvent>() {@Overridepublic void handle(ActionEvent event) {

try {int ival = Integer.parseInt(tf1.getText());Fibonacci task = new Fibonacci(ival);lbl1.textProperty().bind(task.messageProperty());

task.setOnRunning((succeesesEvent) -> {btnStart.setDisable(true);lbl2.setText("");

});

task.setOnSucceeded((succeededEvent) -> {lbl2.setText(task.getValue().toString());btnStart.setDisable(false);

});

ExecutorService executorService = Executors.newFixedThreadPool(1);

executorService.execute(task);executorService.shutdown();

} catch (NumberFormatException e) {

Recommended