View Full Version : [java] filtrare tabella
Ciao
Sto cercando di capire come si potrebbe fare per filtrare una tabella in java (swing).
Cioè:
ho un oggetto JTable, creato in base a un arraylist; vorrei filtrare il contenuto del JTable in base alla selezione effettuata in un JComboBox esterno.
In base alla selezione effettuata nel JComboBox, andrebbero tolte dalla JTable le righe corrispondenti.
Si può fare?
Ho visto che nella classe JTable è previsto il sort della tabella, ma del filter non ho trovato traccia...
L'unica cosa che mi è venuta in mente è distruggere e ricreare la tabella tutte le volte, modificando la fonte dati; ma è terribile...
Grazie :D
Funziona anche cambiando il modello ma, in accordo all'architettura MVC (o SMA), dovresti agire sulla vista (JTable).
JTable ha tre metodi ad hoc:
getRowCount()
getColumnCount()
getValueAt(int row, int col)
Per definizione questi metodi si rifanno al TableModel. La tabella "standard" mostra tante righe e tante colonne quante sono quello nel modello e restituisce i valori corrispondenti alle righe del modello.
Tutto ciò di cui hai bisogno è determinare, a seguito dell'operazione di filtraggio, quali righe del modello siano da scartare. Restituirai quindi attraverso getRowCount un numero di righe pari al numero di righe nel modello meno quelle scartate dal filtro. Concordemente farai sì che il metodo getValueAt tenga conto della "sparizione" di queste righe.
E il gioco è fatto :).
Grazie mille della risposta :)
Però ho ancora un po' di problemi... :(
Mi da più di una:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
Quello che ho fatto è grossomodo questo:
public class LANTable extends JTable {
private ArrayList _righeOk = new ArrayList();
private ArrayList _source;
public LANTable(ArrayList connList) {
super();
this.setModel(new ConnTableModel(connList));
// [...]
_righeOk.clear();
for (int i = 0; i < _source.size(); i++)
_righeOk.add(new Integer(i));
}
public void Filter(String nomeRete) {
Iterator iter = _source.iterator();
int i = 0;
_righeOk.clear();
while (iter.hasNext()) {
LANConnection x = (LANConnection) iter.next();
if (x.get(x.getKeysArray()[0]).toString().equals(nomeRete))
_righeOk.add(new Integer(i));
}
}
class ConnTableModel extends AbstractTableModel {
public ConnTableModel(ArrayList connList) {
_source = connList;
}
// [...]
public int getRowCount() {
return _righeOk.size();
}
public Object getValueAt(int arg0, int arg1) {
Object risultato = null;
if (_righeOk.contains(new Integer(arg0))) {
LANConnection x = (LANConnection) _source.get(arg0);
risultato = x.get(x.getKeysArray()[arg1]);
}
return risultato;
}
// [...]
}
}
public class LANFrame extends JFrame {
public LANFrame(ArrayList list) {
super("Connessioni LAN");
final ArrayList myList = list;
// [...]
final LANTable myTab = new LANTable(myList);
final JComboBox jcmb = new JComboBox();
jcmb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
myTab.Filter(jcmb.getSelectedItem().toString());
}
});
TreeSet ts = new TreeSet();
String[] k = ((LANConnection)myList.get(0)).getKeysArray();
for (int i = 0; i < myList.size(); i++) {
ts.add(((LANConnection)myList.get(i)).get(k[0]));
}
Iterator iter = ts.iterator();
while (iter.hasNext()) {
jcmb.addItem(iter.next());
}
// [...]
}
}
Poi nel main istanzio un ArrayList e lo passo al costruttore di LANFrame.
Dov'è che sbaglio? :confused:
Inoltre ho un po' di problemi a ricreare la tabella rileggendo l'arraylist da un file (serialized); è corretto valorizzare un attributo di una classe in una sua classe interna? Cioè, nel mio esempio, è corretto valorizzare "_source" all'interno del costruttore della classe "ConnTableModel"?
Grazie :D
E' anche possibile che esista un caso in cui una classe interna possa assegnare un valore ad un campo della classe che la contiene ma nel caso di specie è bizzarro.
Sembrerebbe più coerente definire il modello ConnTableModel come tipo primario, dire:
public class LANTable ... {
...
public LANTable(TableModel model) {
super(model);
}
}
e generare una "normale" tabella:
ConnTableModel model = new ConnTableModel(...);
LANTable table = new LANTable(model);
Le viste dipendono dalla dichiarazione di un modello ma non dalla sua definizione. Per chiarezza, itendo per dichiarazione TableModel e per definizione un qualsiasi oggetto compatibile con TableModel (ad esempio proprio ConnTableModel).
Per l'eccezione NullPointerException occorre controllare la traccia stampata sulla console. Ad occhio non mi sembra di aver visto puntatori a null nel codice (ma non sono mai stato un'aquila :D).
Tutto ciò di cui hai bisogno è determinare, a seguito dell'operazione di filtraggio, quali righe del modello siano da scartare. Restituirai quindi attraverso getRowCount un numero di righe pari al numero di righe nel modello meno quelle scartate dal filtro. Concordemente farai sì che il metodo getValueAt tenga conto della "sparizione" di queste righe.
Ciao.
L'ho rivisto praticamente daccapo, seguendo le tue indicazioni successive, ma questo ancora non mi riesce.
Evidentemente come lo faccio io non va bene :(
Per ora è così:
public class LANTableModel extends AbstractTableModel{
private ArrayList<LANConnection> _source;
private ArrayList<Integer> _righeOk;
public LANTableModel(ArrayList<LANConnection> connList){
_source = connList;
_righeOk = new ArrayList<Integer>();
for(int i=0; i<_source.size(); i++)
_righeOk.add(new Integer(i));
}
public int getRowCount(){
return _righeOk.size();
}
public Object getValueAt(int row, int column){
Object risultato = null;
LANConnection x = null;
if(_righeOk.contains(new Integer(row))){
x = _source.get(row);
risultato = x.get(x.getKeysArray()[column]);
}
return risultato;
}
[...]
}
E poi quando la istanzio provo subito a "filtrarla":
LANTableModel model = new LANTableModel(connList);
LANTable tab = new LANTable(model);
JScrollPane scroll = new JScrollPane(tab);
this.add(scroll);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
model.getRigheOk().remove(0);
Cosa c'è di sbagliato?
Grazie :D
Puoi benissimo applicare il filtro al modello (TableModel) ma quello che intendevo io era filtrare la vista: i dati restano quelli che sono, attraverso il filtro se ne vedono solo alcuni. Una cosa del genere:
public interface FiltroTabella {
int getRowCount();
int getColumnCount();
Object getValueAt(int row, int col);
void apply(JTable t);
}
public class TabellaFiltrabile extends JTable {
private FiltroTabella filtro;
public void setFiltro(FiltroTabella t) {
filtro = t;
if(filtro != null) filtro.apply(this);
}
public int getRowCount() {
return filtro == null ? super.getRowCount() : filtro.getRowCount();
}
public int getColumnCount() {
return filtro == null ? super.getColumnCount() : filtro.getColumnCount();
}
public Object getValueAt(int r, int c) {
return filtro == null ? super.getValueAt(r, c) : filtro.getValueAt(r, c);
}
}
Sono anch'io dell'idea che il filtro deva tener traccia delle delle righe valide: una specie di mappa tra gli indici delle righe visibili ed il loro corrispondente del modello.
Ti ringrazio molto della pazienza e della disponibilità; quello che mi proponi è interessante :D
Però c'è una cosa a monte che vorrei capire.
Per il momento prendiamo per buono il mio sistema di scegliere le righe "buone" all'interno della classe TableModel; supponiamo che faccia così:
public LANTableModel(ArrayList<LANConnection> connList){
_source = connList;
_righeOk = new ArrayList<Integer>();
for(int i=0; i<_source.size(); i++){
if(i != 1) // NE ESCLUDO UNA DI PROPOSITO
_righeOk.add(new Integer(i));
}
}
// [...]
public int getRowCount(){
return _righeOk.size();
}
public Object getValueAt(int row, int column){
Object risultato = null;
LANConnection x = null;
if(_righeOk.contains(new Integer(row))){
x = _source.get(row);
risultato = x.get(x.getKeysArray()[column]);
}
return risultato;
}
così il metodo getValueAt restitusce un null nel caso della riga di indice 1.
Questo genera una NullPointerException.
Come devo implementare allora questo metodo, per fargli "saltare" la riga in questione?
Grazie :D
Forse abbiamo visto l'ArrayList in modo diverso :D
Quando scorri il modello per stabilire quali righe siano valide e quali no e immetti nell'ArrayList gli indici delle righe valide, l'ArrayList diventa una mappa di valori (X, Y = arrayList.get(N)), con X indice della riga richiesta dalla tabella e Y indice della riga "reale" nel modello.
Il tuo getValueAt diviene pertanto:
public Object getValueAt(int row, int column) {
int realRow = _righeOk.get(row);
return model.getValueAt(realRow, column);
}
Quel "model.getValueAt(realRow, column)" deve essere inteso nel senso di "il valore che sarebbe stato restituito se non fosse stato applicato alcun filtro".
Grazie mille.
Quello che non capivo proprio era cosa restituire al posto della riga scartata dal filtro.
Ho risolto così:
public Object getValueAt(int row, int column){
Object risultato = null;
LANConnection x = null;
if(row < _righeOk.size()){
row = Integer.valueOf(_righeOk.get(row));
}
x = _source.get(row);
risultato = x.get(x.getKeysArray()[column]);
return risultato;
}
Ora ho problemi con la grafica :(
La tabella è in un jscrollpane, che filtrandola sclera, rimane traccia dello spazio che avanza...
Ho cercato di capire come funziona il repaint, ma non ci riesco... :fagiano:
Sono andato prima per tentativi, con la tabella, il jscrollpane e il frame, poi ho cercato di leggere questo (http://java.sun.com/products/jfc/tsc/articles/painting/index.html#swing), ma non c'ho ricavato un granché... :stordita:
Grazie di nuovo :D
Dovresti invocare, dopo aver applicato il filtro, il metodo revalidate() della tabella (JTable) incriminata.
Fantastico!
Non so come ringraziarti!
:D
vBulletin® v3.6.4, Copyright ©2000-2026, Jelsoft Enterprises Ltd.