Java Tutorial - Parte 2 0.1
Un tutorial per esempi
Caricamento in corso...
Ricerca in corso...
Nessun risultato
Versione 0.7: Il pannello delle proprietà

Introduzione

La applicazione MasterMind comincia ad avere una certa funzionalità e può essere eseguita con diverse opzioni di gioco: è possibile cambiare il tipo di simboli, la lunghezza della sequenza, il nome ed il tipo dei giocatori, etc...

java -ea mastermind.game.Main playerName=Lukas opponentType=AI k=4 codex=COLORS

Tuttavia, dover digitare sulla riga di comando una serie di opzioni delle quali dobbiamo anche ricordarci la sintassi è davvero noioso, poco professionale e, sopratutto, error-prone (=incline agli errori). Se fosse una applicazione CLI (=a riga di comando) allora non abbiamo molte altre possibilità ma in una applicazione GUI dobbiamo fare di meglio.
La soluzione è quella di scrivere un pannello delle proprietà da visualizzare prima che la partita cominci e che può essere attivato dallo user clikkando il bottone "Options" dello splash-screen. Nella figura seguente potete vedere come si presenterà il pannello in argomento :

I nuovi files sorgente

I sorgenti di questa versione sono riepilogati nella tabella seguente:

nome file package descrizione
PropertyPanel.java mastermind.gui il pannello delle proprietà
TestPropertyPanel.java mastermind.test.gui app di test del pannello proprietà
Main07.java mastermind.app classe principale versione 0.7

Il pannello delle proprietà è diviso in tre "schede", racchiuse in un bordo nero con titolo ed ognuna delle tre schede si riferisce ad una specifica categoria di proprietà:

  • Params permette di selezionare i parametri di gioco e altre opzioni di visualizzazione
  • Players permette di selezionare i giocatori
  • Remote permette di selezionare i parametri della connessione remota

Noterete che non tutte le proprietà della applicazione possono essere selezionate dal pannello omonimo:

  • le proprietà di servizio come per esempio il livello di log non interessano all'utente; queste continuano ad essere disponibili alla modifica sulla command-line
  • alcune proprietà come il tipo di giocatore di sinistra non sono selezionabili dal momento che si presume che uno dei due giocatori sia sempre un umano ed è quello di sinistra; è possibile indicare un nickname (=nomignolo).

Il layout del pannello proprietà

Il pannello proprietà viene disposto in un BoxLayout e diviso in quattro pannelli disposti verticalmente: i tre pannelli da cui si selezionano i valori delle proprietà più un pannello per i bottoni di comando "Play" ed "Exit":

  ----BoxLayout.Y_AXIS -----------
 |  - Params -----------------    |
 | |                          |   |
 | |                          |   |
 |  --------------------------    |
 |  -Players -----------------    |
 | |                          |   |
 | |                          |   |
 |  --------------------------    |
 |  -Remote ------------------    |
 | |                          |   |
 | |                          |   |
 |  --------------------------    |
 |--------------------------------|
 |           vertical_glue        |
 |--------------------------------|
 |       PLAY   EXIT              |
  --------------------------------

Il sottopannello dei parametri di gioco

Il sottopannello dei parametri di gioco contiene i tre parametri strettamente collegati al gioco del Mastermind ed altre proprietà che riguardano comunque il gioco. Nella seguente tabella si riepilogano le etichette della proprietà, il componente Swing usato per modificare la proprietà ed una breve descrizione:

Etichetta Componente Descrizione
Number of Symbols JList il numero di simboli a disposizione
Sequence length JRadioButton la lunghezza della sequenza
Symbols Type JComboBox il tipo di simboli (NUMBER,LETTERS,COLORS)
Allow repetition JCheckBox consenti la ripetizione dei simboli
Swap first turn JCheckBox scambia il primo turno nelle partite multiple

Il componente JList

Usiamo il componente Swing JList per il parametro di gioco "numero di simboli a disposizione". Una JList (il nome del componente è list-box) presenta all'utente un gruppo di elementi, visualizzati in una o più colonne, tra cui scegliere. La JList può contenere molti elementi, quindi essa viene spesso visualizzata in uno JScrollPane (= pannello scorrevole). Il codice per visualizzare la lista è il seguente:

// File: PropertyPanel.java
... omissis ...
// le costanti degli elementi della lista
protected static String[] nValues = { "0", "6", "7", "8", "9", "10" };
... omissis ...
protected JPanel createParamsPanel() throws PropertyException
{
... omissis ...
JLabel label = new JLabel( "Number of symbols" );
nList = new JList<String>( nValues );
nList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
nList.setLayoutOrientation( JList.HORIZONTAL_WRAP );
nList.setSelectedValue( properties.getProperty( "n" ), true );
nList.setVisibleRowCount( 1 );
panel.add( label );
panel.add( nList );
... omissis ...

La JList viene costruita passando come argomento una array di stringhe che rappresenta i valori che l'user può selezionare dalla lista; per il parametro di gioco "n" i valori selezionabili sono i numeri da "6" a "10" ma ho previsto anche il valore ZERO che ha un significato particolare: se il giocatore seleziona "0" come parametro "n" allora la applicazione sceglierà un valore casuale tra quelli ammissibili. Il componente JList ha diverse caratteristiche impostabili dal programmatore:

  • un orientamento che si imposta col metodo setLayoutOrientation; nel nostro caso l'orientamento è HORIZONTAL_WRAP (=orizzontale con eventuale ritorno a capo)
  • una modalità di selezione impostabile col metodo setSelectionMode nel nostro caso impostiamo SINGLE_SELECTION che significa che un solo elemento della lista può essere selezionato in ogni momento
  • con il metodo setVisibleRowCount si può impostare il numero di righe che si possono visualizzare: poichè la nostra lista ha un orientamento orizzontale e tutti gli elementi dovrebbero essere contenuti in una unica riga, impostiamo questo valore a UNO.

Infine, impostiamo come "già selezionato" il valore attuale di "n" usando il metodo setSelectedValue. Altri metodi molto utili del componente JList sono:

  • getSelectedIndex che ritorna l'indice nella array dell'elemento selezionato
  • getSelectedValue che ritorna il valore dell'elemento selezionato
  • clearSelection che cancella la selezione
  • isSelectionEmpty che ritorna TRUE se nessun elemento è selezionato

Per maggiori informazioni sull'uso del componente vedi il tutorial ufficiale al seguente link: How to Use Lists

Il componente JRadioButton

Usiamo il componente Swing JRadioButton per la proprietà "sequence length" (=lunghezza della sequenza) I compomenti radio-button (=pulsanti di opzione) sono gruppi di pulsanti in cui, per convenzione, è possibile selezionare un solo pulsante alla volta. La libreria Swing supporta i pulsanti di opzione con le classi JRadioButton e ButtonGroup. In un certo senso, i radio-button assomigliano ad una list-box ma i primi rinforzano il concetto di selezione singola: se viene selezionato un elemento tutti gli altri che fanno parte dello stesso gruppo sono automaticamente deselezionati.
Ogni radio-button è un componente a se stante quindi per inserirli tutti nella scheda dei parametri di gioco dobbiamo creare un sotto-pannello ed iterare su tutti i valori possibili creando, per ognuno di essi, un componente JRadioButton specifico:

// File: PropertyPanel.java
... omissis ...
protected JPanel createParamsPanel() throws PropertyException
{
... omissis ...
label = new JLabel( "Sequence length:" );
JPanel sub = new JPanel();
ButtonGroup group = new ButtonGroup();
kRadio = new JRadioButton[kValues.length];
String selection = properties.getProperty( "k" );
for ( int i = 0; i < kValues.length; i++ ) {
kRadio[i] = new JRadioButton( kValues[i] );
sub.add( kRadio[i] );
group.add( kRadio[i] );
if ( kValues[i].equals( selection )) {
kRadio[i].setSelected( true );
}
}
panel.add( label );
panel.add( sub );

Anche per il parametro di gioco "k" ho previsto, oltre ai due valori ammissibili ("3" e "4") lo ZERO: questo ha lo stesso significato già visto per il parametro "n" e cioè, se il giocatore seleziona lo "0" come parametro di gioco "k" allora la applicazione ne scelgierà uno a caso tra quelli ammissibili.
I metodi più usati del componente JRadioButton sono:

  • setText: imposta il testo del bottone
  • getText: ritorna il testo del bottone
  • setSelected: imposta lo stato del bottone (selezionato o meno)
  • isSelected: ritorna TRUE se il bottone è selezionato

Per maggiori informazioni sull'uso del componente vedi il tutorial ufficiale al seguente link: How to Use Radio Buttons

Il componente JComboBox

Il parametro "symbol type" (=tipo di simbolo) non è un vero parametro di gioco ma, piuttosto, una preferenza del giocatore che può scegliere in che modo visualizzare i codici della sequenza (NUMBERS, LETTERS, COLORS). Per questa proprietà ho scelto il componente Swing JComboBox (una combo-box = casella combinata). Una combo-box consente all'utente di scegliere tra diverse opzioni e può avere due forme molto diverse: la forma predefinita è la casella combinata non modificabile, che presenta un pulsante ed una drop-down list (= un elenco a discesa di valori). La seconda forma, chiamata casella combinata modificabile, presenta un campo di testo con un piccolo pulsante adiacente. L'utente può digitare un valore nel campo di testo o fare clic sul pulsante per visualizzare un elenco a discesa.
Le combo-box occupano poco spazio sullo schermo e il loro formato modificabile (campo di testo) è utile per consentire all'utente di scegliere rapidamente un valore ma senza limitarsi ai valori visualizzati. Poichè la proprietà "symbol type" prevede solo tre valori possibili, la combo-box di questo parametro ha il campo editabile disabilitato.

// File: PropertyPanel.java
... omissis ...
// le costanti degli elementi della combo-box
protected static String[] symbolValues = { "NUMBERS", "LETTERS", "COLORS" };
protected JPanel createParamsPanel() throws PropertyException
{
... omissis ...
label = new JLabel( "Symbols type:" );
// istanzia la combo-box
symbolCombo = new JComboBox<String>( symbolValues );
// disabilita il campo editabile
symbolCombo.setEditable( false );
// imposta l'elemento attualmente selezionato
symbolCombo.setSelectedItem( properties.getProperty( "codex" ));
... omissis ...

La combo-box viene costruita con una array che rappresenta i possibili valori selezionabili dalla lista a discesa. Dopo aver disabilitato il campo editabile, il codice imposta nella combo l'elemento già selezionato in base alle proprietà della applicazione. Infine, aggiunge il componente al sotto-pannello.
I metodi più usati del componente JComboBox sono:

  • addItem inserisce un nuovo elemento alla lista drop-down
  • insertItemAt inserisce un nuovo elemento alla lista drop-down ad uno specifico indice
  • removeItem rimuove un elemento dalla lista drop-down
  • getSelectedItem ritorna l'elemento selezionato oppure editato

Per maggiori informazioni sull'uso del componente vedi il tutorial ufficiale al seguente link: How to Use Combo Boxes

Il componente JCheckBox

Le proprietà "allow repetition" (=consenti ripetizione dei simboli) e "swap first turn" (=scambia il primo turno di gioco) sono rappresentate dal componente Swing JCheckBox. La classe JCheckBox supporta i pulsanti di controllo. I pulsanti di controllo sono simili ai pulsanti di opzione (JRadioButton) ma il loro modello di selezione è diverso, per convenzione: è possibile selezionare un numero qualsiasi di pulsanti di controllo in un gruppo: nessuno, alcuni o tutti. Un gruppo di pulsanti di opzione, invece, può avere un solo pulsante selezionato.
I metodi più usati del componente JCheckBoxBox sono:

  • setText: imposta il testo del bottone
  • getText: ritorna il testo del bottone
  • setSelected: imposta lo stato del bottone (selezionato o meno)
  • isSelected: ritorna REUW se il bottone è selezionato

Per maggiori informazioni sull'uso del componente vedi il tutorial ufficiale al seguente link: How to Use CheckBoxes Boxes

Il sottopannello dei giocatori

Il sottopannello dei giocatori permette di selezionare il nome del giocatore umano ed il tipo di giocatore dell'avversario che può anche essere un altro umano.
Benchè piuttosto improbabile che accada, questa implementazione di Mastermind offre la possibilità per un umano di giocatore contro un umano anche sulla stessa macchina come abbiamo visto in Umano contro umano; Nella seguente tabella vengono riespilogati, per ogni proprietà, la etichetta che la descrive, il componente GUI utilizzato e una breve descrizione:

etichetta componente descrizione
Nickname JTextField un semplice campo di test editabile
Opponent JComboBox il tipo di avversario (anche HUMAN)

Il sottopannello dei giocatori viene creato dal metodo createPlayerPanel il quale istanzia i due componenti descritti nella tabella precedente e li aggiunge al sottopannello.

Il componente JTextField

La proprietà "Player name" permette di specificare il nome del giocatore umano che, per impostazione di default è null. Per specificare questa proprietà si è scelto il componente Swing JTextField: che è un controllo di testo di base che consente all'utente di digitare una piccola quantità di testo. Quando l'utente indica che l'inserimento del testo è completo (solitamente premendo nvio), il campo di testo attiva un ActionEvent.
Il componente JTextField non consente di digitare il testo su più di una riga ma questo non è un problema: il nome del giocatore non dovebbe essere composto di due o più righe.

// File: PropertyPanel.java
protected JPanel createPlayersPanel() throws PropertyException
{
... omissis ...
JLabel label = new JLabel( "Player name:" );
nickname = new JTextField( properties.getProperty("playerName" ));
panel.add( label );
panel.add( nickname );
... omissis ...

Per creare il componente JTextField è sufficente istanziare la classe passando il testo che deve essere visualizzato all'inizio come argomento al costruttore. Successivamente è sufficiente aggiungere il componente ad un pannello per renderlo visibile ed il lavoro termina qui: è davvero uno dei componenti più semplici da usare, al pari delle etichette (le JLabel). I metodi più usati del componente JTextField sono:

  • setText imposta il testo nel componente
  • getText ottiene il testo digitato dallo user
  • setFont imposta il font del componente

Per quanto riguarda il font usato nel componente JTextField vi è da osservare che il font impostato si applica a tutto il testo nel componente e non solo ad una parte di esso.
La libreria Swing di mette a disposizione due varianti specializzate di caselle di testo:

  • JFormattedTextField: è uguale alla sua classe base ma il prorgammatore può specificare dettagliatamente i caratteri che possono esservi digitati
  • JPasswordField: è uguale alla sua classe base ma i caratteri digitati non sono visibili dallo user

Per maggiori informazioni sull'uso del componente vedi il tutorial ufficiale al seguente link: How to Use Text Fields

Il componente JComboBox

La proprietà "Opponent type" viene rappresentata da un componente JComboBox con campo editabile disabilitato esattamente come la proprietà "Symbols type" che abbiamo già analizzato in Il componente JComboBox.

Il sottopannello dei parametri di rete

Anche se al momento la funzionalità di connessione remota non è ancora stata implementata, disegnamo comunque la interfaccia grafica che permette al giocatore di specificare i parametri di rete per la connessione remota. Poichè al momento sarà implementata la sola connessione TCP/IP, le proprietà che l'user deve indicare nel sotto-pannello della connessione riguardano i seguenti parameti:

  • "server" o "client": poichè la architettura scelta per le connessioni remote è quella client/server (vedi La organizzazione della applicazione) è necessario che l'user specifichi il lato della connessione; la selezione dell'uno esclude sempre l'altro: o si è dal lato client oppure dal lato server
  • "server hostname": nel caso di lato client, il giocatore dovrà digitare il nome o l'indirizzo IP del host che funge da server; questo componente sarà disabilitato dal lato server
  • "server port": questo componente rappresenta la porta TCP/IP del server a cui connettersi dal lato client oppure la porta di ascolto dal lato server; il componente è sempre abilitato

Nella seguente tabella vengono riespilogati, per ogni proprietà, la etichetta che la descrive, il componente GUI utilizzato e una breve descrizione:

etichetta componente descrizione
server JRadioButton selezionato per il server di rete
client JRadioButton selezionato per il client di rete
server hostname JTextField il nome host o indrizzo IP del server
server port JSpinner la porta di ascolto o di connessioen

Il componente JRadioButton

Per le proprietà "server" e "client" si usano due JRadioButton inseriti in un ButtonGroup che ne garantisce la selezione esclusiva. Il componente radio-button è già stato descritto in Il componente JRadioButton.

Il componente JTextField

La proprietà "Server hostname" permette al giocatore dal lato client della connessione remota di specificare il nome host o l'indirizzo IP del lato server del gioco. Si tratta di un componente JTextField in cui l'user può inserire una piccola porzione di testo ed è già stato descritto in Il componente JTextField.
Il nome del server da indicare dipende dalla connessione: potrebbe essere un server centralizzato oppure un host o indirizzo IP sulla rete locale (LAN). Esempi di host da inserire in questo campo possono essere:

    mastermind.galdom.net       // server centralizzato
    topolino                    // host sulla LAN, presuppone la presenza i un server DNS
    192.168.1.40                // indirizzo IP sulla LAN

Il componente JSpinner

Il codice per creare il sottopannello dei parametri di rete è molto simile a quello degli altri due sotto-pannelli. Riporto solo l'estratto che riguardo lo spinner per la selezione della porta TCP/IP:

// File: PropertyPanel.java
protected JPanel createNetPanel() throws PropertyException
{
... omissis ...
label = new JLabel( "Server port:" );
netParamsPanel.add( label );
portSpinner = new JSpinner(
new SpinnerNumberModel( properties.getPropertyInt("serverPort", 18862), 1024, 65535, 1 ));
netParamsPanel.add( portSpinner );
... omissis ...

Gli spinner sono simili alle caselle combinate (JComboBox) ed agli elenchi (JList) in quanto consentono all'utente di scegliere tra un intervallo di valori. Come le caselle combinate modificabili, gli spinner consentono all'utente di digitare un valore. A differenza delle caselle combinate, gli spinner non hanno un elenco a discesa. Poiché gli spinner non visualizzano i valori possibili (è visibile solo il valore corrente), vengono spesso utilizzati al posto di caselle combinate o elenchi quando l'insieme di valori possibili è estremamente ampio. Tuttavia, gli spinner dovrebbero essere utilizzati solo quando i valori possibili e la loro sequenza sono evidenti.
Uno spinner è un componente composto con tre sottocomponenti: due piccoli pulsanti e un editor. L'editor può essere qualsiasi JComponent, ma per impostazione predefinita è implementato come un pannello che contiene un campo di testo formattato.

Lo spinner è il componente ideale per specificare il numero di porta TCP dal momento che consente di dare un forte feedback allo user su quali sono i valori ammessi:

  • usando il modello SpinnerNumberModel possono essere inseriti solo valori numerici: la porta TCP è un numero, infatti
  • il valore massimo per un numero di porta è 65.535 e lo spinner ci consente di specificare nel costruttore del suo data-model un limite massimo
  • le porte TCP/IP da 0 a 1023 sono porte privilegiate nei sistemi Unix™-like e poichè possiamo specificare anche un limite minimo nel data-model dello spinner ne cogliamo l'occasione per evitare di usare porte privilegiate per un giochino per bambini

Riepilogando, lo spinner per la porta TCP è costruito in questo modo:

SpinnerModel numberModel =
new SpinnerNumberModel( 18862, // valore attuale
1024, // valore minimo
65535, // valore massimo
1 ); // passo
portSpinner = new JSpinner( numberModel );

I metodi più usati del componente JSpinner sono:

  • setValue imposta il valore dello spinner
  • getValue ottiene il valore dello spinner
  • setModel imposta il data-model dello spinner
  • getModel ottiene il data-model dello spinner

Per maggiori informazioni sull'uso del componente vedi il tutorial ufficiale al seguente link: How to Use Spinners

Leggere le proprietà dal pannello

Dopo che l'user ha scelto i parametri di gioco e le altre proprietà della applicazione, dobbiamo leggere le scelte del giocatore dai vari componenti. Per ottenere questo ci sono sostanzialmente due strategie:

  • la più ovvia è quella di intercettare tutti gli eventi di modifica di un componente e ottenerne il valore modificato
  • la seconda strategia è quella di fare tutto alla fine, quando l'user clikka il bottone "Play"

Intercettare gli eventi

Quando il valore di un componente Swing viene modificatoo viene accodato nella coda degli eventi un oggetto derivato da EventObject il quale descrive l'evento occorso. Abbiamo già affrontato questo argomento nella sezione Il gestore degli eventi nella quale abbiamo imparato che per intercettare un qualsiasi evento è sufficente registrarsi come listener di quel particolare evento per quello specifico componente.
In sostanza, se vogliamo essere notificati quando l'user seleziona un elemento dalla lista dei "numero di simboli a disposizione" dobbiamo creare la JList e successivamente aggiungere la classe che implementa il listener all'elenco dei listeners:

// File: PropertyPanel.java
... omissis ...
protected JPanel createParamsPanel() throws PropertyException
{
... omissis ...
JList list = new JList<String>( nValues );
list.addListSelectionListener( this );
... omissis ...
}
/* Necessario per ListSelectionListener interface */
public void valueChanged(ListSelectionEvent e)
{
... omissis ...
}

Nella tabella seguente riepilogo il componente, la interfaccia da implementare per il listener e l'evento da gestire:

Componente Evento Listener
JList ListSelectionEvent ListSelectionListener
JRadioButton ActionEvent ActionListener
JComboBox ActionEvent ActionListener
JCheckBox ActionEvent ActionListener
JTextField ActionEvent ActionListener
JSpinner ChangeEvent ChangeListener

Il vantaggio di questo approccio consiste nel fatto che non devo tenere traccia dei riferimenti ai componenti istanziati ed aggiunti al pannello: posso ottenere il valore selezionato dallo user nell'evento che ho intercettato nel listener. Lo svantaggio è che per ogni componente devo registrare un listener ma, come potete vedere dalla tabella, i listener sono solo di tre tipi diversi.

Leggere le proprietà alla fine

Con questo approccio posso disinteressarmi dei listeners: in fondo, cosa mi importa delle singole selezioni del giocatore? Posso lasciare che l'user selezioni, deselezioni e modifichi i parametri anche cento volte senza doverne registrare i valori: ciò che conta è che io ottenga i valori selezionati quando il giocatore clikka il bottone "Play", cioè alla fine delle operazioni.
Accanto al vantaggio della assenza dei listeners, con questo approccio ho lo svantaggio di dover tenere traccia di tutti i componenti istanziati nei metodi createXxxxPanel dal momento che, alla fine, dovrò usare quei riferimenti per ottenere i valori:

public class PropertyPanel implements ActionListener
{
... omissis ...
// Le check-box di selezione dei parametri
protected JCheckBox repeat, swapturn, enableUDP;
// Il nickname del giocator umano
protected JTextField nickname;
// Le combo-box di selezione dei parametri
protected JComboBox<String> opponentCombo, symbolCombo;
// I radio buttons del tipo di connessione remota
protected JRadioButton serverBtn, clientBtn;
// Il numero di porta per la connessione remota
protected JSpinner portSpinner;
// La listbox per il parametro 'n'
protected JList<String> nList;
// I componenti check-box per il parametro 'k'
protected JRadioButton[] kRadio;
... omissis ...
// Legge tutte le proprietà dai componenti.
public void readProperties()
{
// parametri di gioco
properties.setPropertyString( "n", nList.getSelectedValue());
properties.setProperty( "repeat", repeat.isSelected());
properties.setProperty( "swapturn", swapturn.isSelected());
properties.setPropertyString( "codex", symbolCombo.getSelectedItem().toString());
... omissis ...
}

Quale dei due approcci è stato scelto? Il secondo, quello in cui tutte le proprietà vengono lette alla fine delle operazioni di selezione da parte dello user e non perchè sia il più efficente o il più comodo, anzi. Il motivo di questa scelta è più sfuggente, più subdolo: supponiamo che l'user modifichi diversi parametri e proprietà ma che alla fine decida di non giocare più e chiude la applicazione. Egli si aspetta che le proprietà originali, quelle in essere prima della visualizzazione del pannello delle proprietà siano ancora valide ma poichè ad ogni selezione le abbiamo modificate ciò che si aspetta l'user non è più vero.
Si potrebbe ovviare a questo inconveniente creando una copia delle proprietà originali nel costruttore del pannello delle proprietà da ripristinare nel caso l'user non intenda più giocare: a parte il fatto che dobbiamo intercettare la chiusura della applicazione abbiamo anche un altro problema. Saremo tentati di creare la copia in questo modo:

oldProperties = properties;

ma non funziona! Non abbiamo affatto creato una copia dell'oggetto MMProperties ma un semplice secondo riferimento ALLO STESSO OGGETTO. Non ha importanza se apportiamo le modifiche a properties oppure a oldProperties perchè entrambi si riferiscono alla unica istanza esistente. Non possiamo creare copie di oggetti, quindi? Certo che possiamo creare copie di qualsiasi oggetto ma con il codice dedicato, non certo con l'operatore di uguaglianza:

public class MyObject
{
private int alfa;
public MyObject( int alfa ) { this.alfa = alfa; }
// crea e restituisce una copia di questo oggetto
public MyObject getCopy()
{
MyObject theCopy = new MyObject( this.alfa );
return theCopy;
}
}

Il metodo getCopy crea una NUOVA ISTANZA della classe MyObject con l'operatore new: è questa istruzione che crea la copia effettiva; ora ci sono davvero due istanze separate della classe MyObject che contengono lo stesso dato. La modifica di una delle due istanze non ha alcun effetto sull'altra.

Il listener degli eventi

Questa sezione vi sembrerà inutile dal momento che non siamo interessati ai valori selezionati in ogni momento dallo user avendo scelto di ottenere tutte le proprietà alla fine delle scelte, quando il giocatore clikka il bottone "Play". E' vero, non siamo interessati ai valori scelti dal giocatore in ogni momento ma siamo interessati ad alcuni di questi valori per dare allo user un forte feedback nelle scelte:

  • se la proprietà "Opponent type" è diversa da REMOTE, il sotto-pannello dei parametri di rete dovrebbe essere disabilitato
  • quando l'user clikka uno dei due radio-buttons "server" e "client" la selezione dovrebbe abilitare la proprietà "server hostname" nel caso sia selezionato "client"; se è selezionato "server" la casella di testo dovrebbe essere disabilitata
  • se viene selezionato il lato client della connessione remota i parametri di gioco dovrebbero essere disabilitati poichè è il server che li stabilisce

Ecco perchè dobbiamo intercettare gli eventi di questi tre componenti e scrivere i metodi che gestiscono la abilitazione 7 disabilitazione dei componenti a seconda della scelta dello user:

// File: PropertyPanel.java
public void actionPerformed(ActionEvent evt)
{
String action = evt.getActionCommand();
if ( "opponent".equals( action )) {
opponentChanged();
}
else if ( "server".equals( action )) {
serverSide();
}
else if ( "client".equals( action )) {
clientSide();
}
}

La classe principale

La classe principale Main07, derivata da Main06 in modo da mantenerne le funzionalità preesistenti, sovrascrive i metodi che implementano le nuove funzionalità:

  • il metodo actionPerformed: è necessario gestire l'evento button-clikked del bottone "Options"
  • il metodo newGame: è necessario leggere le proprietà prima di creare una nuova istanza dell'oggetto game
  • il metodo terminate: prima di chiudere la applicazione, viene richiamato il metodo Propertypanel.close il quale è deputato a liberare le risorse eventualmente allocate.

Al momento attuale, il pannello delle proprietà non alloca alcuna risorsa che deve essere liberata o chiusa ma in futuro questo metodo dovrà essere implementato davvero. Ne riparliamo nel capitolo Version 0.9: la ricerca dei giocatori in rete.

Ulteriore documentazione

La documentazione completa del package descritto in questo capitolo può essere visualizzata clikkando il seguente link che riporta alla documentazione Javadoc della versione 0.7 del progetto: The MasterMind Project Version 0.7

Argomento precedente - Argomento successivo - Indice Generale