Argomenti della Parte 2
- Creazione di una model class
- Uso della model class in una ObservableList
- Visualizzazione dei dati nella TableView usando un Controllers
Creare la Model Class
Abbiamo bisogno della model class per tenere le informazioni sulle persone nella nostra rubrica. Aggiungere una nuova classe al model package (ch.makery.address.model
) chiamata Person
. La classe Person
avrà alcune variabili d’istanza per il nome, l’indirizzo e il compleanno. Aggiungere il seguente codice alla classe. Spiegerò alcune cose specifiche di JavaFX dopo il codice.
Person.java
package ch.makery.address.model; import java.time.LocalDate; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; /** * Model class for a Person. * * @author Marco Jakob */ public class Person { private final StringProperty firstName; private final StringProperty lastName; private final StringProperty street; private final IntegerProperty postalCode; private final StringProperty city; private final ObjectProperty<LocalDate> birthday; /** * Default constructor. */ public Person() { this(null, null); } /** * Constructor with some initial data. * * @param firstName * @param lastName */ public Person(String firstName, String lastName) { this.firstName = new SimpleStringProperty(firstName); this.lastName = new SimpleStringProperty(lastName); // Some initial dummy data, just for convenient testing. this.street = new SimpleStringProperty("some street"); this.postalCode = new SimpleIntegerProperty(1234); this.city = new SimpleStringProperty("some city"); this.birthday = new SimpleObjectProperty<LocalDate>(LocalDate.of(1999, 2, 21)); } public String getFirstName() { return firstName.get(); } public void setFirstName(String firstName) { this.firstName.set(firstName); } public StringProperty firstNameProperty() { return firstName; } public String getLastName() { return lastName.get(); } public void setLastName(String lastName) { this.lastName.set(lastName); } public StringProperty lastNameProperty() { return lastName; } public String getStreet() { return street.get(); } public void setStreet(String street) { this.street.set(street); } public StringProperty streetProperty() { return street; } public int getPostalCode() { return postalCode.get(); } public void setPostalCode(int postalCode) { this.postalCode.set(postalCode); } public IntegerProperty postalCodeProperty() { return postalCode; } public String getCity() { return city.get(); } public void setCity(String city) { this.city.set(city); } public StringProperty cityProperty() { return city; } public LocalDate getBirthday() { return birthday.get(); } public void setBirthday(LocalDate birthday) { this.birthday.set(birthday); } public ObjectProperty<LocalDate> birthdayProperty() { return birthday; } }
Spiegazioni
- Con JavaFX è usuale usare le
Properties
per tutti i campi di una model class. LeProperty
ci permettono, per esempio, di avere una notifica automatica quandolastName
o qualunque altra variabile vengono modificate. Questo ci aiuta a tenere sincronizzato ciò che è visualizzato con i dati. Per saperne di più sulleProperties
leggere Using JavaFX Properties and Binding. LocalDate
, il tipo che usiamo perbirthday
, è parte delle nuove Date and Time API for JDK 8.
Una lista di Persons
I principali dati che la nostra applicazione gestisce sono un gruppo di persone. Creiamo una lista di oggetti Person
all’interno della classe MainApp
. Tutti agli altri controller della classi avranno in seguito accesso alla lista centrale all’interno della classe MainApp
.
ObservableList
Stiamo lavorando con le classi di visualizzazione JavaFX che hanno bisogno di essere informate su eventuali modifiche apportate alla lista delle persone. Questo è importante, poiché altrimenti la vista non sarebbe sincronizzata con i dati. Per questo scopo, JavaFX ha introdotto alcune nuove Collection classes.
Di quelle “Collections”, utiliziamo una ObservableList
. Per creare una nuova ObservableList
, aggiungere il seguente codice all’inizio della classe MainApp
. Dobbiamo inoltre aggiungere un costruttore che crei alcuni dati di esempio e un metodo getter pubblico:
MainApp.java
// ... AFTER THE OTHER VARIABLES ... /** * The data as an observable list of Persons. */ private ObservableList<Person> personData = FXCollections.observableArrayList(); /** * Constructor */ public MainApp() { // Add some sample data personData.add(new Person("Hans", "Muster")); personData.add(new Person("Ruth", "Mueller")); personData.add(new Person("Heinz", "Kurz")); personData.add(new Person("Cornelia", "Meier")); personData.add(new Person("Werner", "Meyer")); personData.add(new Person("Lydia", "Kunz")); personData.add(new Person("Anna", "Best")); personData.add(new Person("Stefan", "Meier")); personData.add(new Person("Martin", "Mueller")); } /** * Returns the data as an observable list of Persons. * @return */ public ObservableList<Person> getPersonData() { return personData; } // ... THE REST OF THE CLASS ...
Il PersonOverviewController
Abbiamo bisogno di un controller per il nostro PersonOverview.fxml
.
- Creare una normale classe dentro il view package chiamata
PersonOverviewController.java
. (Dobbiamo metterla nello stesso package diPersonOverview.fxml
, altrimenti SceneBuilder non la troverà. - Aggiungeremo alcune variabili d’istanza che ci daranno accesso alla tabella e alle labels all’interno della vista. I campi e alcuni metodi hanno una speciale annotazione
@FXML
. Questa è necessaria per il file fxml per avere accesso ai campi e metodi privati. Dopo aver impostato tutto nel file fxml, l’applicazione popolerà automaticamente le variabili quando il file fxml sarà caricato. Aggiungiamo il seguente codice:
PersonOverviewController.java
package ch.makery.address.view; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import ch.makery.address.MainApp; import ch.makery.address.model.Person; public class PersonOverviewController { @FXML private TableView<Person> personTable; @FXML private TableColumn<Person, String> firstNameColumn; @FXML private TableColumn<Person, String> lastNameColumn; @FXML private Label firstNameLabel; @FXML private Label lastNameLabel; @FXML private Label streetLabel; @FXML private Label postalCodeLabel; @FXML private Label cityLabel; @FXML private Label birthdayLabel; // Reference to the main application. private MainApp mainApp; /** * The constructor. * The constructor is called before the initialize() method. */ public PersonOverviewController() { } /** * Initializes the controller class. This method is automatically called * after the fxml file has been loaded. */ @FXML private void initialize() { // Initialize the person table with the two columns. firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty()); lastNameColumn.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty()); } /** * Is called by the main application to give a reference back to itself. * * @param mainApp */ public void setMainApp(MainApp mainApp) { this.mainApp = mainApp; // Add observable list data to the table personTable.setItems(mainApp.getPersonData()); } }
Adesso questo codice ha probabilmente bisogno di alune spiegazioni:
- Tutti i campi e i metodi a cui il file fxml ha bisogno di accedere devono essere annotati con
@FXML
. Attualmente, solo se sono privati, ma è meglio averli privati e comunque marcarli con l’annotazione! - Il metodo
initialize()
è automaticamente chiamato dopo che il file fxml è stato caricato. In questo momento, tutti i campi FXML dovrebbero essere già inizializzati. setCellValueFactory(...)
che impostiamo sulle colonne della tabella è usato per determinare quale campo all’interno dell’oggettoPerson
dovrebbe essere usato per quella particolare colonna. La freccia->
indica che stiamo usando una caratteristica di Java 8 chiamata Lambdas. (Un’altra opzione potrebbe essere quella di usare una PropertyValueFactory, ma non è un tipo sicuro).
In questo esempio stiamo usando solo un valore StringProperty
per le colonne. Quando si vuole usare un IntegerProperty
o un DoubleProperty
, setCellValueFactory(...)
deve avere anche un asObject()
:
myIntegerColumn.setCellValueFactory(cellData -> cellData.getValue().myIntegerProperty().asObject());
Questo è necessario per una cattiva scelta di design di JavaFX (vedi questa discussione).
Connessione di MainApp con PersonOverviewController
Il metodo setMainApp(...)
deve essere chiamato dalla classe MainApp
. Questo ci dà modo di accedere ad un oggeto MainApp
e di prendere una lista di Persons
e altre cose. Sostituire il metodo showPersonOverview()
con il seguente. Questo contiene due linee aggiuntive:
MainApp.java - nuovo metodo showPersonOverview()
/** * Shows the person overview inside the root layout. */ public void showPersonOverview() { try { // Load person overview. FXMLLoader loader = new FXMLLoader(); loader.setLocation(MainApp.class.getResource("view/PersonOverview.fxml")); AnchorPane personOverview = (AnchorPane) loader.load(); // Set person overview into the center of root layout. rootLayout.setCenter(personOverview); // Give the controller access to the main app. PersonOverviewController controller = loader.getController(); controller.setMainApp(this); } catch (IOException e) { e.printStackTrace(); } }
Agganciare la vista al Controller
Ci siamo quasi! Ma manca una piccola cosa: Non abbiamo ancora detto al nostro file PersonOverview.fxml
quale controller usare e come accoppiare elemento e campo all’interno del controller .
Aprire
PersonOverview.fxml
con SceneBuilder.Aprire il gruppo Controller sul lato sinistro e selezionare
PersonOverviewController
come controller class.
Selezionare la
TableView
nel gruppo Hierarchy e selezionarepersonTable
nel campo fx:id.
Fare lo stesso per le colonne selezionando rispettivamente
firstNameColumn
elastNameColumn
nel campo fx:id.Per ogni label nella seconda colonna, scegliere il corrispondente fx:id.
Importante: Tornare su Eclipse e e aggiornare l’intero progetto (F5). Questo è necessario perchè alcune volte Eclipse non rileva i cambiamenti apportati in Scene Builder.
Avviare dell’applicazione
Se avvii l’applicazione adesso dovresti vedere qualcosa di simile allo screenshot all’inizio del post.
Congratulazioni!
Nota: Le label non si aggiornano ancora quando viene selezionata una persona. Gestiremo l’interazione con l’utente nella prossima parte del tutorial
What’s Next?
Nella Parte 3 del tutorial aggiungeremo nuove funzionalità per aggiungere, cancellare e modificare una persona.