Java Swing: un esempio di utilizzo del GridBagLayout per la realizzazione di un form

In Java spesso la realizzazione di interfacce grafiche via codice attraverso l’utilizzo dei LayoutManager crea parecchi problemi nell’ottenere il posizionamento desiderato per i componenti. Per questo motivo spesso vengono utilizzati a tale scopo degli ambienti grafici, come ad esempio NetBeans, che sfruttando il LayoutManager GroupLayout permettono di creare in modo facile e veloce le interfacce grafiche trascinando semplicemente i componenti nel più classico degli approcci WYSIWYG (What You See Is What You Get).

Qualora si voglia invece procedere “a mano” nella creazione delle interfacce grafiche occorre scegliere il LayoutManager da utilizzare, ovvero il componente che gestisce il posizionamento dei componenti grafici e le aree che essi andranno ad occupare. In questo articolo vedremo un esempio di utilizzo del GridBagLayoutManager, la cui adozione viene caldamente raccomandato dalla documentazione Oracle che lo definisce come “uno dei più flessibili, potenti e complessi layout managers che la piattaforma Java fornisca.”
Il GridBagLayout dispone i componenti in una griglia di righe e colonne di dimensione variabile a seconda dell’altezza e della larghezza dei componenti che occupano le varie celle. Non c’è quindi un vincolo sulla dimensione di righe e colonne. Per posizionare un componente basta fornire al LayoutManager GridBagLayout le coordinate della griglia in cui quel componente deve essere inserito. Eventualmente è possibile specificare un padding esterno per il componente, con il quale il GridBagLayout inserisce uno spazio tra il componente ed i bordi della sua cella. Inoltre, qualora fosse necessario, è possibile indicare al GridBagLayout che un determinato componente deve estendersi per più colonne oppure deve occupare più righe. Queste ed altre proprietà possono essere gestite componente per componente tramite un oggetto GridBagConstraints che viene fornito come parametro al metodo add quando si va ad aggiungere il componente al JPanel.

Per analizzare il funzionamento del GridBagLayout procediamo con un esempio. Creiamo un form per l’inserimento dei dati di un utente che prevede anche la selezione di un file da allegare.
Il form che andremo a realizzare avrà questo aspetto:

GridBagLayout form example

Inoltre aggiungeremo dei controlli di validazione per verificare che tutti i valori siano stati inseriti e, per ogni campo non correttamente valorizzato, mostreremo un messaggio di errore vicino al componente.
Di seguito viene illustrata l’interfaccia del nostro form nel caso in cui l’utente prema il pulsante “Submit” senza aver inserito nessun dato:

GridBagLayout Swing example

Come possiamo vedere dalla figura seguente, la disposizione dei componenti segue una struttura a griglia, in cui per ciascuna riga abbiamo 3 colonne contenenti la label che indica il nome del campo, il controllo per l’inserimento del valore e lo spazio per l’eventuale messaggio di errore. Rappresentano delle piccole eccezioni la gestione dei controlli per la selezione del file, in cui il messaggio di errore viene posizionato sotto, in una riga interamente dedicata a lui ed il bottone per la sottomissione del file che viene posizionato, da solo, nella colonna centrale dell’ultima riga (da notare anche la maggiore distanza a cui è posizionato, sulla quale torneremo in seguito).

GridBagLayout row and columns

Vediamo come creare questo form in Java con Swing.
Iniziamo creando la classe che rappresenta la nostra view, cioè quella che contiene i componenti grafici. Creiamo nel nostro progetto il package “view” e creiamo una classe che possiamo chiamare GridBagLayoutFormView alla quale facciamo estendere JPanel.

package view;

public class GridBagLayoutFormView extends JPanel { 

}

A questo punto iniziamo a dichiarare come membri di istanza tutti i componenti che ci serviranno per realizzare la nostra interfaccia grafica. Li definiamo con il modificatore di visibilità private ed inseriamo i rispettivi getters e setters.

package view;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class GridBagLayoutFormView extends JPanel {

	private JLabel lFileName;
	private JTextField tfFile;
	private JButton fcButton;
	private JLabel lFileError;

	private JLabel lName;
	private JTextField tfName;
	private JLabel lNameError;

	private JLabel lLastName;
	private JTextField tfLastName;
	private JLabel lLastNameError;

	private JLabel lAddress;
	private JTextField tfAddress;
	private JLabel lAddressError;

	private JLabel lZipCode;
	private JTextField tfZipCode;
	private JLabel lZipCodeError;

	private JLabel lCountry;
	private JComboBox cbCountry;
	private JLabel lCountryError;

	private JLabel lEmail;
	private JTextField tfEmail;
	private JLabel lEmailError;

	private JLabel lWebsite;
	private JTextField tfWebsite;
	private JLabel lWebsiteError;

	private JButton submitButton;
	
	// GETTERS AND SETTERS

	public JLabel getlFileName() {
		return this.lFileName;
	}

	public void setlFileName(JLabel lFileName) {
		this.lFileName = lFileName;
	}

	public JTextField getTfFile() {
		return this.tfFile;
	}

	public void setTfFile(JTextField tfFile) {
		this.tfFile = tfFile;
	}

	// others getters and setters have been omitted
		
}

Procediamo inserendo nella nostra classe della view il suo costruttore, al quale facciamo richiamare il metodo build() che si occuperà del posizionamento vero e proprio degli elementi, utilizzando il GridBagLayout.

public GridBagLayoutFormView () {
	this.build();
}
	
private void build() {
	// to be implemented
}

Nel metodo build() creiamo la nostra interfaccia grafica.
Per prima cosa indichiamo al nostro panel che vogliamo utilizzare GridBagLayout come LayoutManager per il posizionamento dei nostri componenti. Per farlo utilizziamo il metodo setLayout():

this.setLayout(new GridBagLayout());

Poi creiamo l’oggetto GridBagConstraints che ci permetterà di specificare la cella della griglia in cui inserire i componenti ed altre proprietà relative al loro posizionamento. Utilizziamo un unico oggetto GridBagConstraints per tutti gli elementi da posizionare nel nostro panel, modificando quando necessario le proprietà prima dell’aggiunta del singolo componente.

GridBagConstraints gbc = new GridBagConstraints();

Ok, possiamo iniziare a creare e disporre i componenti del nostro form.
Iniziamo con i componenti della prima riga, quella in cui abbiamo la label per il campo in cui dobbiamo scegliere un file, il campo di testo che conterrà il nome del file ed il pulsante per l’apertura della finestra per la selezione del file. La JLabel, come possiamo vedere dall’immagine 3, con la griglia dei componenti si trova nella cella di coordinate (0,0), cioè prima riga e prima colonna, il JTextField alle coordinate (0,1) ed il JButton nella cella (0,2).
Per indicare in quale cella del GridBagLayout vogliamo posizionare il componente dobbiamo utilizzare le proprietà gridx e gridy del nostro oggetto GridBagConstraints.

private void build() {
	this.setLayout(new GridBagLayout());
	GridBagConstraints gbc = new GridBagConstraints();

	// Row 0 - Filename
		// Col 0
	this.lFileName = new JLabel("Filename:");
	gbc.gridx = 0;
	gbc.gridy = 0;
	gbc.insets = new Insets(5, 0, 0, 10);
	gbc.anchor = GridBagConstraints.LINE_END;
	this.add(this.lFileName, gbc);

		// Col 1
	this.tfFile = new JTextField(20);
	this.tfFile.setEditable(false);
	gbc.gridx = 1;
	gbc.gridy = 0;
	gbc.anchor = GridBagConstraints.LINE_START;
	this.add(this.tfFile, gbc);

		// Col 2
	this.fcButton = new JButton("Browse");
	gbc.gridx = 2;
	gbc.gridy = 0;
	gbc.anchor = GridBagConstraints.LINE_START;
	this.add(this.fcButton, gbc);
}

Le altre proprietà dell’oggetto GridBagConstraints che abbiamo utilizzato nello spezzone di codice precedente sono:
insets, alla quale abbiamo assegnato un’istanza di Insets che è un oggetto che definisce uno spazio che il componente deve lasciare tra se ed il bordo all’interno di cui è contenuto. Si tratta praticamente di un padding esterno, definito sui 4 lati del componente, a partire dall’alto.
Il costruttore di Insets è definito infatti come: Insets(int top, int left, int bottom, int right).
Come si può vedere la proprietà insets l’abbiamo definita solo per la label mentre per gli altri 2 componenti non compare nel codice. Questo perché non ci serviva modificare il suo valore e, siccome l’oggetto GridBagConstraints utilizzato è lo stesso anche per gli altri componenti, l’Insets definito rimane.
anchor, che indica quale allineamento utilizzare nel caso il componente sia più piccolo dello spazio che ha a disposizione nella sua cella. Si può indicare, tramite l’utilizzo di apposite constanti definite nella classe GridBagConstraints un allineamento orizzontale al centro, a destra oppure a sinistra ed un allineamento verticale in alto, al centro oppure in basso.
I possibili valori di queste costanti ed la relativa indicazione di posizionamento dei componenti sono indicati nella figura seguente:
GridBagConstraints anchor values

Nelle versioni di Java precedenti alla 1.4 si utilizzavano altre costanti che definiva la posizione in termini di “NORTH, EAST, WEST, NORTHEAST, ecc.” ma la documentazione Oracle suggerisce di utilizzare le nuove costanti illustrate nella figura sopra. La documentazione dice infatti:
“Version note: The PAGE_* and *LINE_* constants were introduced in 1.4. Previous releases require values named after points of the compass. For example, NORTHEAST indicates the top-right part of the display area. We recommend that you use the new constants, instead, since they enable easier localization.”

In questo caso abbiamo utilizzato l’allineamento “LINE_END” per la label in modo che essa sia allineata a destra ed invece quello “LINE_START” per il textfield ed il pulsante, in modo che fossero allineati a sinistra.

Tornando alla figura 3 che illustra la griglia che definisce il nostro form vediamo che nella seconda riga è presente un solo componente, la label di segnalazione dell’errore nel caso in cui non sia stato selezionato nessun file. Tale componente è presente nella cella con x=1 ed y=1. Le celle in posizione 0 e 3 di questa riga rimarranno invece vuote.
Vediamo quindi il codice da aggiungere per posizionare questo componente:

// Row 1 - File not selected Error
	// Col 0 - Empty

	// Col 1
this.lFileError = new JLabel("Please select a file.");
this.lFileError.setForeground(Color.RED);
this.lFileError.setVisible(false);
gbc.gridx = 1;
gbc.gridy = 1;
gbc.anchor = GridBagConstraints.LINE_START;
this.add(this.lFileError, gbc);

	// Col 2 - Empty

Per le label di segnalazione di errore settiamo il colore del testo a rosso e le definiamo di default non visibili, in quanto dovranno comparire solo nel caso in cui, alla pressione del pulsante “Submit” da parte dell’utente, il relativo campo non sia stato valorizzato.

La riga successiva prevede nuovamente 3 elementi, 2 label ed un campo di testo:

// Row 2 - Name
	// Col 0
this.lName = new JLabel("First Name:");
gbc.gridx = 0;
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.LINE_END;
gbc.insets = new Insets(15, 0, 0, 10);
this.add(this.lName, gbc);

	// Col 1
this.tfName = new JTextField(15);
gbc.gridx = 1;
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.LINE_START;
this.add(this.tfName, gbc);

	// Col 2
this.lNameError = new JLabel("Please insert your First Name.");
this.lNameError.setForeground(Color.RED);
this.lNameError.setVisible(false);
gbc.gridx = 2;
gbc.gridy = 2;
gbc.anchor = GridBagConstraints.LINE_START;
this.add(this.lNameError, gbc);

Come possiamo vedere, nell’inserimento del primo di questi 3 elementi, abbiamo sovrascritto la proprietà “insets” dell’oggetto GridBagConstraints, aumentando il valore del primo parametro, in modo da aumentare lo spazio lasciato tra il componente che stiamo posizionando ed il componente disposto sopra ad esso.

Procedendo in modo analogo per tutti gli altri componenti otteniamo il seguente codice come versione finale della nostra classe GridBagLayoutFormView contenente la view del nostro form:

package view;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

import controller.GridBagController;

public class GridBagLayoutFormView extends JPanel {

	private JLabel lFileName;
	private JTextField tfFile;
	private JButton fcButton;
	private JLabel lFileError;

	private JLabel lName;
	private JTextField tfName;
	private JLabel lNameError;

	private JLabel lLastName;
	private JTextField tfLastName;
	private JLabel lLastNameError;

	private JLabel lAddress;
	private JTextField tfAddress;
	private JLabel lAddressError;

	private JLabel lZipCode;
	private JTextField tfZipCode;
	private JLabel lZipCodeError;

	private JLabel lCountry;
	private JComboBox cbCountry;
	private JLabel lCountryError;

	private JLabel lEmail;
	private JTextField tfEmail;
	private JLabel lEmailError;

	private JLabel lWebsite;
	private JTextField tfWebsite;
	private JLabel lWebsiteError;

	private JButton submitButton;

	private void build() {
		this.setLayout(new GridBagLayout());
		GridBagConstraints gbc = new GridBagConstraints();

		// Row 0 - Filename
			// Col 0
		this.lFileName = new JLabel("Filename:");
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.insets = new Insets(5, 0, 0, 10);
		gbc.anchor = GridBagConstraints.LINE_END;
		this.add(this.lFileName, gbc);

			// Col 1
		this.tfFile = new JTextField(20);
		this.tfFile.setEditable(false);
		gbc.gridx = 1;
		gbc.gridy = 0;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.tfFile, gbc);

			// Col 2
		this.fcButton = new JButton("Browse");
		gbc.gridx = 2;
		gbc.gridy = 0;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.fcButton, gbc);

		// Row 1 - File not selected Error
			// Col 0 - Empty

			// Col 1
		this.lFileError = new JLabel("Please select a file.");
		this.lFileError.setForeground(Color.RED);
		this.lFileError.setVisible(false);
		gbc.gridx = 1;
		gbc.gridy = 1;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.lFileError, gbc);

			// Col 2 - Empty
		
		
		// Row 2 - Name
			// Col 0
		this.lName = new JLabel("First Name:");
		gbc.gridx = 0;
		gbc.gridy = 2;
		gbc.anchor = GridBagConstraints.LINE_END;
		gbc.insets = new Insets(15, 0, 0, 10);
		this.add(this.lName, gbc);

			// Col 1
		this.tfName = new JTextField(15);
		gbc.gridx = 1;
		gbc.gridy = 2;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.tfName, gbc);

			// Col 2
		this.lNameError = new JLabel("Please insert your First Name.");
		this.lNameError.setForeground(Color.RED);
		this.lNameError.setVisible(false);
		gbc.gridx = 2;
		gbc.gridy = 2;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.lNameError, gbc);

		// Row 3 - Last Name
			// Col 0
		this.lLastName = new JLabel("Last Name:");
		gbc.gridx = 0;
		gbc.gridy = 3;
		gbc.anchor = GridBagConstraints.LINE_END;
		gbc.insets = new Insets(5, 0, 0, 10);
		this.add(this.lLastName, gbc);

			// Col 1
		this.tfLastName = new JTextField(15);
		gbc.gridx = 1;
		gbc.gridy = 3;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.tfLastName, gbc);

			// Col 2
		this.lLastNameError = new JLabel("Please insert your last name.");
		this.lLastNameError.setForeground(Color.RED);
		this.lLastNameError.setVisible(false);
		gbc.gridx = 2;
		gbc.gridy = 3;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.lLastNameError, gbc);

		// Row 4 - Address
			// Col 0
		this.lAddress = new JLabel("Address:");
		gbc.gridx = 0;
		gbc.gridy = 4;
		gbc.anchor = GridBagConstraints.LINE_END;
		gbc.insets = new Insets(15, 0, 0, 10);
		this.add(this.lAddress, gbc);

			// Col 1
		this.tfAddress= new JTextField(20);
		gbc.gridx = 1;
		gbc.gridy = 4;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.tfAddress, gbc);

			// Col 2
		this.lAddressError = new JLabel("Please insert your address.");
		this.lAddressError.setForeground(Color.RED);
		this.lAddressError.setVisible(false);
		gbc.gridx = 2;
		gbc.gridy = 4;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.lAddressError, gbc);

		// Row 5 - ZipCode
			// Col 0
		this.lZipCode = new JLabel("Zip Code:");
		gbc.gridx = 0;
		gbc.gridy = 5;
		gbc.anchor = GridBagConstraints.LINE_END;
		gbc.insets = new Insets(5, 0, 0, 10);
		this.add(this.lZipCode, gbc);

			// Col 1
		this.tfZipCode = new JTextField(6);
		gbc.gridx = 1;
		gbc.gridy = 5;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.tfZipCode, gbc);

			// Col 2
		this.lZipCodeError = new JLabel("Please insert your zip code.");
		this.lZipCodeError.setForeground(Color.RED);
		this.lZipCodeError.setVisible(false);
		gbc.gridx = 2;
		gbc.gridy = 5;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.lZipCodeError, gbc);

		// Row 6 - Country
			// Col 0
		this.lCountry = new JLabel("Country:");
		gbc.gridx = 0;
		gbc.gridy = 6;
		gbc.anchor = GridBagConstraints.LINE_END;
		this.add(this.lCountry, gbc);

			// Col 1
		String []countries = {"", "Italy", "UK", "USA"};
		this.cbCountry = new JComboBox(countries);
		gbc.gridx = 1;
		gbc.gridy = 6;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.cbCountry, gbc);

			// Col 2
		this.lCountryError = new JLabel("Please select your country.");
		this.lCountryError.setForeground(Color.RED);
		this.lCountryError.setVisible(false);
		gbc.gridx = 2;
		gbc.gridy = 6;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.lCountryError, gbc);

		// Row 7 - Email
			// Col 0
		this.lEmail = new JLabel("Email:");
		gbc.gridx = 0;
		gbc.gridy = 7;
		gbc.anchor = GridBagConstraints.LINE_END;
		gbc.insets = new Insets(15, 0, 0, 10);
		this.add(this.lEmail, gbc);

			// Col 1
		this.tfEmail = new JTextField(15);
		gbc.gridx = 1;
		gbc.gridy = 7;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.tfEmail, gbc);

			// Col 2
		this.lEmailError = new JLabel("Please insert your email.");
		this.lEmailError.setForeground(Color.RED);
		this.lEmailError.setVisible(false);
		gbc.gridx = 2;
		gbc.gridy = 7;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.lEmailError, gbc);

		// Row 8 - Website
			// Col 0
		this.lWebsite = new JLabel("Website:");
		gbc.gridx = 0;
		gbc.gridy = 8;
		gbc.anchor = GridBagConstraints.LINE_END;
		gbc.insets = new Insets(5, 0, 0, 10);
		this.add(this.lWebsite, gbc);

			// Col 1
		this.tfWebsite = new JTextField(15);
		gbc.gridx = 1;
		gbc.gridy = 8;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.tfWebsite, gbc);

			// Col 2
		this.lWebsiteError = new JLabel("Please insert your website.");
		this.lWebsiteError.setForeground(Color.RED);
		this.lWebsiteError.setVisible(false);
		gbc.gridx = 2;
		gbc.gridy = 8;
		gbc.anchor = GridBagConstraints.LINE_START;
		this.add(this.lWebsiteError, gbc);

		// Row 9 - Submit button
			// Col 0 empty

			// Col 1
		this.submitButton = new JButton("Submit");
		gbc.gridx = 1;
		gbc.gridy = 9;
		gbc.anchor = GridBagConstraints.LINE_START;
		gbc.insets = new Insets(25, 0, 0, 10);

		this.add(this.submitButton, gbc);

	}

	
	// CONSTRUCTOR
	public GridBagLayoutFormView () {
		this.build();
	}

	// GETTERS AND SETTERS

	public JLabel getlFileName() {
		return this.lFileName;
	}

	public void setlFileName(JLabel lFileName) {
		this.lFileName = lFileName;
	}

	// others getters and setters have been omitted
}

Facciamo ancora qualche considerazione sul layout del nostro form e sul relativo codice appena visto. Notiamo ad esempio che i campi del nostro form sono “raggruppati” in 3 gruppi; il primo composto dai campi “First Name” e “Last Name”, il secondo composto dalle informazioni geografiche ed il terzo composto dalle informazioni di contatto “email” e “website”. La suddivisione viene fatta aumentando leggermente la distanza tra i componenti dei vari gruppi. La distanza tra “Address” e “Last Name” infatti è maggiore di quella tra “First Name” e “Last Name” ed allo stesso modo tra “Country” ed “Email” c’è uno spazio ulteriore a separare i due gruppi di campi.
Vediamo come abbiamo ottenuto questo risultato nel codice. Nel primo elemento della riga 4, quella relativa al campo “Address” modifichiamo l’oggetto GridBagConstraints aumentando il valore del padding in alto, definito dal primo parametro dell’oggetto Insets con cui valorizziamo la proprietà insets:

// Row 4 - Address
	// Col 0
this.lAddress = new JLabel("Address:");
gbc.gridx = 0;
gbc.gridy = 4;
gbc.anchor = GridBagConstraints.LINE_END;
gbc.insets = new Insets(15, 0, 0, 10);   // increase the top padding
this.add(this.lAddress, gbc);

Abbiamo quindi aumentato lo spazio che la riga 4 deve lasciare rispetto a quella sopra ad essa e poi, siccome vogliamo che la successiva “Zip Code” faccia parte del suo “gruppo” e sia quindi “vicina” ad essa, abbiamo ridotto nuovamente il padding alto per gli elementi della riga 5:

// Row 5 - ZipCode
	// Col 0
this.lZipCode = new JLabel("Zip Code:");
gbc.gridx = 0;
gbc.gridy = 5;
gbc.anchor = GridBagConstraints.LINE_END;
gbc.insets = new Insets(5, 0, 0, 10);	// decrease the top padding again
this.add(this.lZipCode, gbc);

Ok, ora dobbiamo provare la nostra classe GridBagLayoutFormView. Creiamo quindi un package che chiamiamo “example” ed al suo interno creiamo una nuova classe che chiamiamo “GridBagLayoutExample” che conterrà un main in cui creiamo un frame all’interno del quale inseriamo il pannello rappresentato dalla nostra GridBagLayoutFormView.

package example;

import javax.swing.JFrame;

import view.GridBagLayoutFormView;

public class GridBagLayoutExample {

    public static void main(final String[] args) {
        final JFrame frame = new JFrame();
        final GridBagLayoutFormView myPanel = new GridBagLayoutFormView();
        frame.setSize(700, 400);
        frame.setTitle("GridBagLayout Example");
        frame.getContentPane().add(myPanel);
        frame.setVisible(true);
    }
}

Eseguendo il programma otteniamo il nostro form:

GridBagLayout form example

A questo punto non ci resta che effettuare la validazione dei campi. Per farlo creeremo un semplice listener che attaccheremo al pulsante “Submit” e che una volta premuto tale pulsante controllerà che tutti i dati siano stati inseriti, evidenziando i campi mancanti con una relativa label rossa di segnalazione. Creiamo quindi all’interno del nostro progetto un nuovo package che chiamiamo “controller” nel quale creiamo la classe “GridBagController” che implementerà l’interfaccia ActionListener.
Un esempio di controller per la validazione del nostro form potrebbe essere il seguente:

package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTextField;

import view.GridBagLayoutFormView;

public class GridBagController implements ActionListener {

    private final GridBagLayoutFormView gble;  // the view

    public GridBagController(final GridBagLayoutFormView gble) {
        this.gble = gble;
    }

    @Override
    public void actionPerformed(final ActionEvent e) {
        // reset all error labels
        this.resetLabels();

        boolean fileAvailable = false;

        // check for file selection
        if (this.gble.getTfFile().getText().equals("")) {
            updateErrorLabel(this.gble.getlFileError(),
                             "Please select a file",
                             true);
        }
        else
            fileAvailable = true;

        // Check for all the others fields
        if (this.checkAllInputs() && fileAvailable)
            System.out.println("Everything in OK!");
        else 
            System.out.println("There are some errors in your form!");

    }

	// Reset of the error labels
    protected void resetLabels() {
        updateErrorLabel(this.gble.getlFileError(), "", false);
        updateErrorLabel(this.gble.getlNameError(), "", false);
        updateErrorLabel(this.gble.getlLastNameError(), "", false);
        updateErrorLabel(this.gble.getlAddressError(), "", false);
        updateErrorLabel(this.gble.getlZipCodeError(), "", false);
        updateErrorLabel(this.gble.getlCountryError(), "", false);
        updateErrorLabel(this.gble.getlEmailError(), "", false);
        updateErrorLabel(this.gble.getlWebsiteError(), "", false);
    }

    private boolean checkAllInputs() {
        boolean allFieldsValid = true;

        allFieldsValid &= checkInput(this.gble.getTfName(),
                                     this.gble.getlNameError());
        allFieldsValid &= checkInput(this.gble.getTfLastName(),
                                     this.gble.getlLastNameError());
        allFieldsValid &= checkInput(this.gble.getTfAddress(),
                                     this.gble.getlAddressError());
        allFieldsValid &= checkInput(this.gble.getTfZipCode(),
                                     this.gble.getlZipCodeError());
        allFieldsValid &= checkInput(this.gble.getCbCountry(),
                                     this.gble.getlCountryError());
        allFieldsValid &= checkInput(this.gble.getTfEmail(),
                                     this.gble.getlEmailError());
        allFieldsValid &= checkInput(this.gble.getTfWebsite(),
                                     this.gble.getlWebsiteError());

        return allFieldsValid;
    }

    // Utility function that sets the text and the visibility of JLabel
    protected static void updateErrorLabel(final JLabel ErrorLabel,
                                           final String labelTExt,
                                           final boolean vis) {
        ErrorLabel.setText(labelTExt);
        ErrorLabel.setVisible(vis);
    }

    // Utility function that checks for empty input values
	// returns false is the field is empty, false otherwise
    private static boolean checkInput(final JComponent input,
                                      final JLabel ErrorLabel) {
        boolean res = true;

        if (input instanceof JTextField) {
            final JTextField tmp = (JTextField) input;
            if (tmp.getText().equals("")) {
                updateErrorLabel(ErrorLabel, "Please insert a value.", true);
                res = false;
            }
        }
        else if (input instanceof JComboBox) {
            final JComboBox tmp = (JComboBox) input;
            if (tmp.getSelectedItem().equals("")) {
                updateErrorLabel(ErrorLabel, "Please select a value.", true);
                res = false;
            }
        }
        return res;
    }
}

Agganciamo quindi il nostro listener GridBagController al pulsante “Submit”, aggiungendo nella classe view la riga:

this.submitButton.addActionListener(new GridBagController(this));

in fase di aggiunta del pulsante:

// Row 10 - Submit button
		// Col 1 empty

		// Col 2
this.submitButton = new JButton("Submit");
gbc.gridx = 1;
gbc.gridy = 9;
gbc.anchor = GridBagConstraints.LINE_START;
gbc.insets = new Insets(25, 0, 0, 10);

this.submitButton.addActionListener(new GridBagController(this));

this.add(this.submitButton, gbc);

Inoltre dobbiamo aggiungere anche un listener al pulsante “Browse” che ci permetta., una volta, premuto, di scegliere un file. Modifichiamo quindi ancora la classe “GridBagLayoutFormView” nella parte di creazione di tale pulsante, come segue:

	  // Col 3
this.fcButton = new JButton("Browse");
gbc.gridx = 2;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.LINE_START;
this.fcButton.addActionListener(new ActionListener() {

	@Override
	public void actionPerformed(ActionEvent e) {
		JFileChooser fc = new JFileChooser();
		int returnVal = fc.showOpenDialog(null);

		if (returnVal == JFileChooser.APPROVE_OPTION) {
			GridBagLayoutFormView.this.tfFile.setText(fc.getSelectedFile().getAbsolutePath().toLowerCase());
	}

} }) ;
this.add(this.fcButton, gbc);

A questo punto possiamo lanciare nuovamente il programma ed effettuare un po’ di prove:

1) Non inseriamo nessun dato:
GridBagLayout Swing example
Output: There are some errors in your form!

2) Selezioniamo un file ed inseriamo solo alcuni dati:
Swing GridBagLayout example missing fields
Output: There are some errors in your form!

3) Inseriamo tutti i dati previsti:
GridBagLayout swing example
Output: Everything in OK!

L’intero esempio con tutte le classi definite è scaricabile qui:

Leave a Reply

Your email address will not be published. Required fields are marked *