Un
primo esempio di JTable
La
base di dati da cui partiremo per creare la nostra prima
tabella è composta da:
- Una
matrice di Object:
Object[][]
cells = {{"0,0","0,1","0,2"}, {"1,0","1,1","1,2"}};
- Un'array
di String:
String[]
columnNames = {"Colonna1", "Colonna2", "Colonna3"};
La
matrice contiene naturalmente i dati che vogliamo rappresentati,
ed il fatto che sia composta di Object aumenta di poco
la complessità della sua struttura ma ne amplifica
esponenzialmente le possibilità. Occorre fare
attenzione ed avere sempre presente che classi di oggetti
sono rappresentati nelle varie celle dalla nostra tabella.
L'array di stringhe contiene gli headers (le intestazioni)
delle colonne della tabella.
Vediamo
subito il codice per costruire la nostra prima JTable:
JScrollPane
scrollPane = new JScrollPane();
JTable table;
String[] columnNames = {"Colonna1", "Colonna2",
"Colonna3"};
Object[][] cells = {{"0,0","0,1","0,2"},
{"1,0","1,1","1,2"}};
table
= new JTable(cells, columnNames);
scrollPane.getViewport().add(table);
contentPane.add(scrollPane);
Questa piccola porzione di codice è abbastanza
semplice, e come avevamo detto si è usato un
array per contenere le intestazioni delle colonne (columnNames),
ed una matrice di Object per contenere i dati che saranno
visualizzati (cells).
Semplicemente aggiungendo, ad esempio, lo scrollPane
al contentPane di un JFrame, si ottiene
il seguente risultato:
Figura
1
Il
paradigma Document/View (O MVC)
La
struttura dati che abbiamo usato è abbastanza
semplice e, in generale, non sarà mai tanto più
complessa di questa. Però creare una tabella
così come è stato fatto, dandole in pasto
direttamente la sua struttura dati è un po' come
aprire un file XML direttamente con un browser (ad esempio
Explorer). Quello che succede è che vengono si
mostrati dati, ma senza la possibilità di intervenire
sulla loro visualizzazione. Principalmente per lo stesso
motivo chi ha creato i componenti swing ha dotato i
più complessi di un modello, ossia la possibilità
di specificare come gli eventuali dati mostrati dai
componenti devono essere visualizzati.
Il vantaggio principale della separazione tra documento
e vista è quello di separare il dato dalla sua
visualizzazione, secondo il paradigma Document/View.
Così come nel web, nel quale, su questo paradigma
sono stati definiti standard (nei quali l'XML gioca
la parte da leone), anche in ogni buona applicazione
Java (e non) è buona norma rispettarlo, svincolando
la parte grafica dell'applicazione da tutto quello che
è il reperimento, la formattazione o il calcolo
dei dati.
Il modello della JTable
Un
modello, in questo caso modello di tabella, è
il punto di riferimento per quanto riguarda i dati della
tabella stessa. Ovvero è l'oggetto che gestisce
i dati che la popolano. Infatti la JTable, come il JTree,
non immagazzina lei stessa i dati che visualizza, ma
delega questo, ed altre cose, al suo modello. La
figura mostra un esempio di questa interazione.
Figura
2
Nelle poche righe di codice di cui era composto l'esempio
precedente non ci si era preoccupati di gestire questa
iterazione. In realtà non abbiamo contravvenuto
al paradigma Document/View, in quanto, se non specificato,
il componente JTable usa un modello base che non fa
altro se non quello di passare i dati così come
sono alla tabella.
La classe DefaultTableModel implementa questo modello.
Nell'esempio sotto riportato una sua istanza viene usata
nella creazione della tabella. Il risultato è
assolutamente identico all'esempio precedente:
JScrollPane
scrollPane = new JScrollPane();
JTable table;
String[] columnNames = {"Colonna1", "Colonna2",
"Colonna3"};
Object[][] cells = {{"0,0","0,1","0,2"},
{"1,0","1,1","1,2"}
};
DefaultTableModel
defaultModel = new DefaultTableModel(cells, columnNames);
table = new JTable(defaultModel);
scrollPane.getViewport().add(table);
contentPane.add(scrollPane);
Scrivendo
in questo modo si nota come il modello sia creato partendo
dai dati da rappresentare ed il componente grafico invece
dal suo modello.
AbstractTableModel
La
classe AbstractTableModel fornisce una prima implementazione
dei principali metodi di TableModel. Essa, pertanto,
può essere usata come punto di partenza per la
definizione di un modello adatta alle nostre esigenze.
Sono solo tre i metodi che dobbiamo ridefinire se estendiamo
questo modello:
public
int getRowCount();
public int getColumnCount();
public Object getValueAt(int row, int column);
Attraverso
questi tre metodi la JTable ottiene risposta alle seguenti
domande:
-
Di quante righe è composta la mia base dati
- Di
quante colonne
- Quale
oggetto devo mettere ad una determinata posizione.
Andiamo
avanti per gradi. Riprendiamo l'esempio iniziale sostituendo
il DefaultTableModel con un nostro modello, che chiameremo
SimpleTableModel.
Il codice per creare il SimpleTableModel è il
seguente:
class
SimpleTableModel extends AbstractTableModel{
String[] columnNames = {"Colonna1",
"Colonna2", "Colonna3"};
String[][] cells = {{"0,0","0,1","0,2"},
{"1,0","1,1","1,2"}
};
public int getRowCount(){
return cells.length;
}
public int getColumnCount(){
return columnNames.length;
}
public Object getValueAt(int r, int c){
return cells[r][c];
}
}
A questo punto, la dichiarazione della tabella cambia,
ma non di molto:
TableModel
simpleModel = new SimpleTableModel();
table = new JTable(simpleModel);
Il risultato ottenuto con queste modifiche è
il seguente:
Figura
3
Mancano
i nomi giusti delle colonne. Quelli che vediamo sono
nomi dati di default dal nostro modello, che non ha
avuto da noi informazioni su quali questi siano. Per
fornirgliele è sufficiente l'overriding del metodo
getColumnName, cioè l'aggiunta alla nostra nuova
classe del metodo:
public
String getColumnName(int c){
return columnNames[c];
}
A questo punto il risultato è tornato identico
a quello dell'esecuzione originaria.
Ora
la gestione dei dati è passata tutta al modello.
Vediamo di fargli fare qualche cosa che possa avere
più senso della semplice visualizzazione dei
dati. Costruiamocene uno che ci consenta di avere due
colonne, in cui sono immagazzinati valori interi, ed
una in cui viene mostrata, ad esempio, la somma delle
prime due.
Per fare questo occorre ridefinire il metodo getVaueAt,
in modo che sappia riconoscere il fatto che, nel caso
sia richiesto un valore dell'ultima colonna, deve essere
restituita la sommatoria dei valori di tutte le altre
colonne della stessa riga. Tradotto in codice può
diventare:
class
SimpleTableModel extends AbstractTableModel{
String[] columnNames = {"Valore1",
"Valore2", "Somma"};
Integer[][] cells = {{new Integer(4),
new Integer(5)},
{new
Integer(7), new Integer(8)}
};
public int getRowCount(){
return cells.length;
}
public int getColumnCount(){
return columnNames.length;
}
public Object getValueAt(int r, int c){
if (c < columnNames.length
- 1)
return cells[r][c];
else{
int value = 0;
for (int i = 0;
i < columnNames.length-1; i++){
value += ((Integer)cells[r][i]).intValue();
}
return new Integer(value);
}
}
public String getColumnName(int c){
return columnNames[c];
}
}
Abbiamo visto come gestire i dati per caricare una tabella.
Nelle prossime lezioni vedremo come la JTable li visualizza,
per mezzi dei renderer delle singole celle, e useremo,
come fonte dati, una tabella di un database relazionale.
|