danilo.pianini@unibo.itgianluca.aguzzi@unibo.itangelo.filaseta@unibo.itCompiled on: 2025-12-05 — versione stampabile
Libreria Java per la creazione di GUI per Rich Applications multi-piattaforma
SwipeEvent), in funzione della piattaforma in cui l’applicazione è in esecuzioneWindowStage
Stage può mostrare una sola Scene alla volta: si imposta via Stage#setScene(Scene)Scene#setRoot(Parent)init, start, stop, …)start(Stage) che riceve lo stage primariopublic class App extends javafx.application.Application {
@Override
public void start(Stage stage) throws Exception {
Group root = new Group();
Scene scene = new Scene(root, 500, 300);
stage.setTitle("JavaFX Demo");
stage.setScene(scene);
stage.show();
}
public static void run(String[] args) {
launch(args);
}
}
void main(String[] args) {
App.run(args);
}
main() dentro la classe App (che estende Application) può risultare nel seguente errore: “Error: JavaFX runtime components are missing, and are required to run this application” (richiederebbe l’aggiunta di JavaFX al module path all’avvio dell’applicazione)main in una classe separata da quella dell’applicazione JavaFXL’avvio mediante Application.launch(App.class) comporta:
App (la classe specificata che estende Application)init()start(javafx.stage.Stage) dell’applicazionePlatform.exit()Platform.isImplicitExit() è true)stop() dell’applicazioneNodeParent rappresenta nodi che possono avere figli (recuperabili via getChildren())Node: Canvas, Parent

build.gradle.kts per un progetto JavaFX-basedplugins {
java
application
}
repositories {
mavenCentral()
}
val javaFXModules = listOf("base","controls","fxml","swing","graphics")
java { // Useful to set Java version for Gradle tasks
toolchain { languageVersion.set(JavaLanguageVersion.of(25)) }
}
val javaFxVersion = 25
val supportedPlatforms = listOf("linux", "mac", "win")
dependencies {
for (platform in supportedPlatforms) {
for (module in javaFXModules) {
implementation("org.openjfx:javafx-$module:$javaFxVersion:$platform")
}
}
}
App) deve estendere la classe javafx.application.Applicationvoid start(Stage primaryStage) che è, di fatto, l’entry point dell’applicazione JavaFX (lo stage primario è creato dalla piattaforma)setScene())show()main() dell’applicazione Java, che deve chiamare Application.launch(App.class)Ogni scena ha un root node relativo a una gerarchia di nodi descrivente la GUI
Ciascun nodo (componente) espone diverse proprietà osservabili (classe Property<T>)
size, posizion, color, …)text, value, …)controller, …)Ciascun nodo genera eventi in relazione ad azioni dell’utente
public class Example1 extends Application {
@Override
public void start(Stage stage) throws Exception {
final Label lbl = new Label();
lbl.setText("Label text here...");
final Button btn = new Button();
btn.setText("Click me");
final HBox root = new HBox();
root.getChildren().add(btn);
root.getChildren().add(lbl);
stage.setTitle("JavaFX - Example 1");
stage.setScene(new Scene(root, 300, 250));
stage.show();
}
}
Property<T> è un ObservableValue<T> (un valore ottenibile con getValue() a cui possono essere associati dei ChangeListener via remove/addListener) scrivibile che può essere collegato/scollegato ad altri osservabili o proprietà attraverso
bind(ObservableValue<? extends T> observable) / bindBidirectional(Property<T> other)unbind() / unbindBidirectional(Property<T> other)xxx di tipo T ha (opzionalmente) getter/setter getXxx() e setXxx(), e un metodo xxxProperty() che restituisce un oggetto Property<T>
TextField offre getText():String, setText(String), e textProperty():Property<String>final TextField input = new TextField();
final Label mirror = new Label();
// connette la label con il valore del textfield
mirror.textProperty().bindBidirectional(input.textProperty());
mirror.setText("default");
Parent (nodo che può avere nodi figli – cf. proprietà protected children):
Group (gestisce un insieme di figli; ogni trasformazione/effetto è applicata su ogni figlio)Region (classe base per tutti i controlli UI e i layout)
Control (classe base per tutti i controlli UI)layoutX e layoutY dei Node)BorderPane, HBox/VBox, TilePane, GridPane, FlowPane, AnchorPane, StackPane
ObservableList<Node> getChildren() restituisce la lista di nodi figli di un qualunque nodo/layout
add(Node) e addAll(Node...)) e gestiti i componenti figliGroup g = new Group();
Label l1 = new Label("label");
Button b1 = new Button("a larger button");
Button b2 = new Button("small button");
g.getChildren().addAll(l1, b2, b3);
// Use binding to suitable place the components
b1.layoutXProperty().bind(l1.widthProperty().add(10));
b2.layoutXProperty().bind(b1.layoutXProperty()
.add(b1.widthProperty()).add(10));
g.setTranslateX(-5); // applies translation to all children
g.setEffect(new DropShadow()); // applies effect to all children
Text center = new Text("Center"); // ...
BorderPane bpane = new BorderPane(center, top, right, bottom, left);
bpane.setCenter(new Text("NewCenter"));
Button topLeft = new Button("Top Left");
AnchorPane.setTopAnchor(topLeft, 10.0); // 10px from the top edge
AnchorPane.setLeftAnchor(topLeft, 10.0); // 10px from the left edge
AnchorPane root = new AnchorPane(topLeft);
// An empty vertical TilePane with 5px horiz / 10px vertical spacing
TilePane tp2 = new TilePane(Orientation.VERTICAL, 5, 10);
tp2.setPrefRows(3);
tp.setPrefTileHeight(100);
for(Month m : Month.values()) { tp2.getChildren().add(new Label(m.name())); }
GridPane gp = new GridPane();
gp.setGridLinesVisible(true);
for(Month m : Month.values()) {
Label l = new Label(m.name());
gp.getChildren().add(l);
int columnIndex = (m.getValue()-1) / 4; int rowIndex = (m.getValue()-1) % 4;
GridPane.setConstraints(l, columnIndex, rowIndex);
// OR ALSO: gp.add(l, columnIndex, rowIndex);
}

javafx.geometry.Bounds che espone:
getMinX(), getMinY(), getMinZ()getWidth(), getHeight(), getDepth()getMaxX()… come getMinX()+getWidth()…Scene
Region ma non un Group), allora il ridimensionamento della scena causerà un aggiustamento del layoutStage
Node può essere “gestito” (managed) o meno: nel primo caso, il parent ne gestirà il posizionamento/dimensionamento (in base alla preferred size del nodo)Screen (si veda slide più avanti), i bound degli schermi non-primari saranno relativi a quelli dello schermo primariojavafx.event.Event) possono essere generati dall’interazione dell’utente con gli elementi grafici
consume())T che implementa Event, EventHandler<T> deve implementare il metodo void handle(T)setOn...()Stage all’event target)Button// Handler for mouse click events
public class ClickHandler implements EventHandler<MouseEvent> {
private final Label lbl;
public ClickHandler(Label lbl) { this.lbl = lbl; }
@Override
public void handle(MouseEvent event) {
lbl.setText("Hello, JavaFX World!");
}
}
// General event handler for action events
public class ActionHandler implements EventHandler<ActionEvent> {
private final Label lbl;
public ActionHandler(Label lbl) { this.lbl = lbl; }
@Override
public void handle(ActionEvent event) {
lbl.setText("Hello, JavaFX World!");
}
}
// Wiring the handlers to the button and label
btn.setOnMouseClicked(new ClickHandler(lbl));
btn.addEventHandler(ActionEvent.ACTION, new ActionHandler(lbl));
public class App extends Application {
@Override
public final void start(final Stage mainStage) {
final Scene scene = new Scene(initSceneUI());
mainStage.setScene(scene);
mainStage.setTitle("JavaFX Example");
mainStage.show();
}
private Parent initSceneUI() {
final Label inputLbl = new Label("Input: ");
final TextField inputArea = new TextField();
final Button okBtn = new Button("Open a new Stage with the input data!");
okBtn.setOnMouseClicked(new OpenSecondStageHandler(inputArea));
final BorderPane root = new BorderPane();
root.setRight(okBtn);
root.setLeft(inputLbl);
root.setCenter(inputArea);
BorderPane.setAlignment(inputLbl, Pos.CENTER_LEFT);
BorderPane.setAlignment(okBtn, Pos.CENTER_RIGHT);
return root;
}
public static run(String[] args) {
launch(args);
}
}
public class OpenSecondStageHandler implements EventHandler<MouseEvent> {
private final TextField inputField;
public OpenSecondStageHandler(final TextField inputField) {
this.inputField = inputField;
}
@Override
public void handle(final MouseEvent event) {
new SecondStage(inputField.getText()).show();
}
}
public class SecondStage extends Stage {
private Label lbl;
public SecondStage(final String message) {
super();
setTitle("New Window...");
setScene(new Scene(initSceneUI(), 400, 200));
lbl.setText(message);
}
private Parent initSceneUI() {
lbl = new Label();
FlowPane root = new FlowPane();
root.setAlignment(Pos.CENTER);
root.getChildren().add(lbl);
return root;
}
}
// To run the application
void main(String[] args) {
App.run(args);
}
Application sono eseguiti (ad es. start) oppure no (ad es. init) su JFXATScreen s = Screen.getPrimary();
double dpi = s.getDpi();
Rectangle2D sb = s.getBounds();
Ractangle2D svb = s.getVisualBounds();
ObservableList<Screen> screenList = Screen.getScreens();
Model: modello OO del dominio applicativo del sistemaView: gestisce le interazioni con l’utente (input e output)Controller: gestisce il coordinamento fra Model e View
Model: incapsula dati e logica relativi al dominio della applicazioneView: incapsula la GUI, le sue sottoparti, e la logica di notificaController: intercetta gli eventi della View, comanda le modifiche al modello, cambia di conseguenza la ViewModelInterface: letture/modifiche da parte del ControllerViewObserver: comandi inviati dalla view al controller (void)ViewInterface: comandi per settare la view, notifiche a fronte dei comandi (errori..)DrawNumberDrawNumber: interfaccia del modello che espone i metodi di gioco:
void reset(): resetta il giocoDrawResult drawNumber(int number): esegue una estrazione e restituisce il risultatoDrawNumberView: interfaccia della view che espone i metodi di visualizzazioneDrawNumberViewObserver: interfaccia del controller che espone i comandi della viewDrawNumberElementi di dominio (Model):
DrawNumber: interfaccia del modello che espone i metodi di gioco:
void reset(): resetta il giocoDrawResult drawNumber(int number): esegue una estrazione e restituisce il risultatoElementi di presentazione:
DrawNumberView: interfaccia della view che espone i metodi di visualizzazione
start(): avvia la viewresult(DrawResult result): visualizza il risultato di una estrazionedisplayError(String message): visualizza un messaggio di erroreElementi di controllo:
DrawNumberViewObserver: interfaccia del controller che espone i comandi della view
newAttempt(int number): notifica di una nuova estrazione richiesta dall’utenteresetGame(): notifica di un reset del giocoquitGame(): notifica di uscita dall’applicazioneDrawNumber (UML)
progettare le 3 interfacce
void) chiamati da V, esprimono “azioni utente”void) chiamati da C, esprimono richieste di visualizzazionela tecnologia scelta per le GUI sia interna a V, e mai menzionata altrove o nelle interfacce
implementare separatamente M, V e C, poi comporre e testare
in progetti reali, M, V e C si compongono di varie parti
danilo.pianini@unibo.itgianluca.aguzzi@unibo.itangelo.filaseta@unibo.itCompiled on: 2025-12-05 — versione stampabile