Un primo esempio di JTable Il paradigma Document/View (O MVC) Il modello della JTable AbstractTableModel
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"}}; … … 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 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. 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: Scrivendo in questo modo si nota come il modello sia creato partendo dai dati da rappresentare ed il componente grafico invece dal suo modello. 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: 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: A questo punto, la dichiarazione della tabella cambia, ma non di molto: 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: 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: 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.
La JTable è un componente swing, veramente potente, che consente, se usato in maniera proficua, di ottenere tabelle e visualizzazioni veramente complesse e funzionali. Come tutte le classi quali ad esempio JList, JTree o altro, è possibile farne un utilizzo semplice e veloce, e con poche righe di codice arrivare ad avere una buona visualizzazione dei nostri dati. D'altro canto, con un po' di complessità in più, è possibile creare GUI esattamente come le abbiamo pensate nella nostra testa, o come il nostro grafico le ha disegnate.
Un primo esempio di JTableLa 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, siottiene 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 JTableUn 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:
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.
AbstractTableModelLa 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 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:
public int getColumnCount() {
return columnNames.length;
}
public Object getValueAt(int r, int c){
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(){
public int getRowCount() {
return cells.length;
}
public int getColumnCount(){
public int getColumnCount() {
return columnNames.length;
}
public Object getValueAt(int r, int c){
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){
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.
MokaByte®
è un marchio registrato da MokaByte s.r.l.
Java®, Jini® e tutti i nomi derivati sono marchi
registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione
anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it