fxml

interfacce grafiche definite tramite file xml

A volte è necessario costruire applicazioni con delle interfacce complicate e scrivere tutto il codice che riguarda la finestra manualmente potrebbe essere noioso...

La prima versione crea gli oggetti da programma.

La seconda versione usa invece fxml.

Per fare un primo esperimento è sufficiente inserire i due file "verdi" nello stesso pacchetto (cioè nella stessa cartella) e vedere cosa succede.

File: NomeCognome.java

package it.aspix.librojava.fxml;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class NomeCognomeClassico extends Application{
public class NomeCognomeFxml extends Application{

	TextField campoNome = new TextField();
	TextField campoCognome = new TextField();
	Label risposta = new Label("-");@FXML TextField campoNome;
	@FXML TextField campoCognome;
	@FXML Label risposta;
	
	@Override
	public void start(Stage palcoPrincipale){
		Label etichettaNome = new Label("nome:");
		Label etichettaCognome = new Label("cognome:");
		Button combina = new Button("combina nome e cognome");
		GridPane pannelloPrincipale = new GridPane();
		
		pannelloPrincipale.add(etichettaNome, 0, 0);
		pannelloPrincipale.add(campoNome, 1, 0);
		pannelloPrincipale.add(etichettaCognome, 0, 1);
		pannelloPrincipale.add(campoCognome, 1, 1);
		pannelloPrincipale.add(combina, 0, 2, 2, 1);
		pannelloPrincipale.add(risposta, 0, 3, 2, 1);
		
		// alcune sistemazioni estetiche
		pannelloPrincipale.setHgap(5);
		pannelloPrincipale.setVgap(5);
		pannelloPrincipale.setPadding(new Insets(10));
		combina.setMaxWidth(Double.MAX_VALUE);
		pannelloPrincipale.setId("sfondo");
		
		combina.setOnAction( e->gestioneClickPulsante(e)  );
		
		Scene scena = new Scene(pannelloPrincipale);
		Scene scena = new Scene( FXMLLoader.load(NomeCognomeFxml.class.getResource("NomeCognomePannello.fxml")) );
		palcoPrincipale.setScene(scena);
		palcoPrincipale.setTitle("Nome + Cognome");
		scena.getStylesheets().add("it/aspix/librojava/fxml/stili.css");
		palcoPrincipale.show();
	}
	
	public static void main(String args[]){
		launch(args);
	}
	
	@FXML
	private void gestioneClickPulsante(ActionEvent e){
		String combinato;
		combinato = campoNome.getText()+" "+campoCognome.getText();
		risposta.setText(combinato);
	}
}

File: NomeCognomePannello.fxml

Questo file non deve essere scritto manualmente: può essere costruito usando un apposito programma chiamato SceneBuilder.

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>

<GridPane id="sfondo" hgap="5.0" stylesheets="@stili.css" vgap="5.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="it.aspix.librojava.fxml.NomeCognome">
   <children>
      <Label text="nome:" />
      <Label text="cognome:" GridPane.rowIndex="1" />
      <TextField fx:id="campoNome" GridPane.columnIndex="1" />
      <TextField fx:id="campoCognome" GridPane.columnIndex="1" GridPane.rowIndex="1" />
      <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#gestioneClickPulsante" text="combina nome e cognome" GridPane.columnSpan="2" GridPane.rowIndex="2" />
      <Label fx:id="risposta" text="-" GridPane.columnSpan="2" GridPane.rowIndex="3" />
   </children>
   <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
   </padding>
</GridPane>

File: stili.css

.label {
	-fx-text-fill: yellow;
}

#sfondo {
	-fx-background-color: black;
}

Come procedere

Ovviamente il vantaggio sta nell'avere un programma che non contiene tutta la parte ripetitiva riguardante le interfacce ma se dovessimo scrivere manualmente il file .fxml il guadagno non sarebbe molto. Fortunatamente per editare questo tipo di file possiamo usare Scene Builder che è un editor grafico (meglio avere la stessa versione di Scene Builder e JavaFX).

Passo passo

Proviamo a costruire passo passo un programma che legge un dato da una casella di testo e lo scrive in una label alla pressione di un pulsante. Non lasciarti impressionare dal primo tentativo, una volta imparato questa cosa è terribilmente comoda!

  1. Scriveremo i due file nella stessa cartella/pacchetto
  2. Crea la classe EsempioFXML che estende javafx.application.Application
  3. Nella classe dichiara (senza costruirle!) due variabili: una Label chiamata "output" e una TextField chiamata "input", non devi dichiarare il pulsante!
  4. Metti il marcatore @FXML (javafx.fxml.FXML) prima delle dichiarazioni (stessa riga o riga sopra, fa lo stesso), in questo modo dichiari che quell'oggetto è accessibile da FXML.
  5. Adesso crea il metodo "private void copia()" che legge il contenuto dalla casella e lo copia nell'etichetta.
  6. Marca anche questo metodo come @FXML
  7. Prima roba meccanica: aggiungi il solito metodo main
  8. Seconda roba meccanica: crea anche il metodo "public void start(Stage finestra)" in cui dovrai fare solo 4 cose:
    1. la prima purtroppo è sintatticamente complicata: Scene scena = new Scene( FXMLLoader.load(EsempioFXML.class.getResource("EsempioFXML.fxml")) );
    2. finestra.setScene(scena) come al solito
    3. imposta il titolo come al solito
    4. mostra la finestra come al solito
  9. Adesso apri Scene Builder e scegli una finestra vuota ("empty")
  10. Adesso procedi con calma perché Scene Builder ha tantissimi controlli, prenditi del tempo per guardare tutto quello che c'è
  11. Nel gruppo (a sinistra) "Containers" prendi "GridPane" e trascinalo al centro nell'area grigia, comparirà una griglia con 2 colonne e tre righe, se così non fosse facendo click con il tasto desto su una qualsiasi etichetta di colonna(riga sarà possibile aggiungerne o rimuoverne)
  12. Salva il lavoro attuale nella solita cartella (quella della classe) e chiamalo "EsempioFXML.fxml". Fai attenzione: Eclipse non si accorgerà del file salvato, ogni volta che lo salvi dovrai fare click con il destro sul pacchetto di Eclipse e fare "refresh")
  13. In basso a sinistra in Scene builder trovi "Controller", aprilo e come "Controller class" metti la classe che hai creato in precedenza (questo serve a dire quale classe controllerà l'interfaccia grafica)
  14. Apri a sinistra in "Library" il gruppo "Controls"
  15. Prendi una label e trascinala nel posto 0,0 e cambia il testo contenuto (doppio click!) in "scrivi qualcosa qui:"
  16. Trascina un TextField nel posto 1,0 e poi (sempre con la casella di testo selezionata) sulla destra seleziona il gruppo "code"
  17. Sempre sulla destra selziona "fx:id" e poi "input" (questo dice al sistema quale variabile della classe gestore sarà associata alla casella di testo)
  18. Trascina una label in posizione 1,2 e fai lo stesso procedimento ma come fx:id seleziona "output"
  19. Adesso trascina un Button in 1,1 e metti come testo del pulsante "fai click qui"
  20. Sempre con il pulsanter selezionato sul pannello "code" sulla destra sul campo "On Action" seleziona "copia"
  21. Salva il lavoro su Scene Builder e aggiorna la cartella di Eclipse