# JTable und SelectionListener



## hey-ho (15. Jun 2008)

Hallo,

ich möchte gerne mit einem SelectionListener die selektierte Zeile in einer JTable ausgegeben haben, bzw. den selektierten Datensatz bearbeiten, löschen... Meine Daten kommen aus einer Datenbank, werden über eine Datenklasse in einem Vector gespeichert und dann mittels dem TableModel in die JTable gebracht. Wenn die Tabelle sortiert wird, stimmt der Index der Tabellenzeile ja nicht mehr mit dem Index im Vector überein. Wenn ich nun in den Datenklassen im Vector die ID aus der Datenbank mitspeichere, kann ich dann über den SelectionListener auf diesen Wert zugreifen, auch wenn er nicht in einer Tabellenspalte angezeigt wird, sondern nur über den Vector zu holen wäre? Ich würde - wenn möglich - gerne die ID des Datenbanksatzes an einen Button übergeben, der diese Zeile z.B. dann in der Datenbank löscht.

Ich hoffe, ich hab mich jetzt nicht zu kompliziert augedrückt. Ich hab jetzt schon ewig im Internet nach Lösungen gesucht, werde aber irgendwie nicht richtig schlauer.

Als Beispielcode nun noch meine JTable-Klasse:
(Ist das was ich dort mit dem ListSelectionListener veranstalte überhaupt einigermassen richtig?)


```
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;


public class PersonenTable extends JTable implements ListSelectionListener {
	
	PersonenTableModel personenTableModel;
	
	public PersonenTable(PersonenTableModel personenTableModel) {
		
		this.personenTableModel = personenTableModel;
		
		this.setModel(personenTableModel);
		
		setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		setAutoCreateRowSorter(true);
		
		getTableHeader().setReorderingAllowed( false ); 

	    setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	    
	    getSelectionModel().addListSelectionListener(this);

	    columnModel = getColumnModel();
	    columnModel.getColumn(0).setPreferredWidth(180);
	    columnModel.getColumn(1).setPreferredWidth(180);
	    columnModel.getColumn(2).setPreferredWidth(80);
	    columnModel.getColumn(3).setPreferredWidth(40);
	    
	    DefaultTableCellRenderer rendererRight = new DefaultTableCellRenderer();
	    rendererRight.setHorizontalAlignment(SwingConstants.RIGHT);
	    columnModel.getColumn(2).setCellRenderer(rendererRight);
	    columnModel.getColumn(3).setCellRenderer(rendererRight);

	}
	
	public void valueChanged(final ListSelectionEvent e) {
		int row = getSelectedRow();
		System.out.println("Ausgewählte Zeile: " + row);
		System.out.println("Eintrag in Spalte 4: " + personenTableModel.getValueAt(row,3).toString());

	}

}
```

Seltsam ist, dass mir die Testausgabe auf der Konsole zwar die richtige selektierte Zeile ausgibt, auch den ID-Wert der Datenbank, den ich mal testweise in eine Tabellenspalte gesteckt habe, aber die Ausgabe auf der Konsole wird viermal geschrieben...

Vielen Dank schonmal
hey-ho


----------



## manuche (16. Jun 2008)

Wo ist jetzt das Problem wenn der Listener die richtigen Werte zurück gibt?
Ich versteh nicht so ganz, warum du an den Button die ID weitergeben willst...
Warum lässt du den Button nicht alle Daten zusammenholen sodass er seine Funktion ausführen kann?
Sonst bekommt dein Button ja zuviel wenn einer aus langeweile in den Zeilen der Tabelle rumklickert


----------



## hey-ho (16. Jun 2008)

Mein Problem ist, dass ich nicht genau weiss, welcher Datensatz denn nun selektiert ist, weil der Index der Tabelle nicht mit dem des Vectors oder der Datenbank übereinstimmt, wenn ich die Tabelle neu sortiert habe. Die Spalte mit der ID aus der Datenbank hatte ich ja nur zu Testzwecken hinzugefügt. Ich hab jetzt mal versucht, diese Spalte im TableModel mit getValueAt zwar hinzuzufügen, aber nicht mit anzuzeigen. Das scheint einigermassen zu funktionieren, aber scheint mir irgendwie nicht besonders optimal.

Ich will halt erreichen, dass eine selektierte Zeile mir einen Datensatz aus der Datenbank löscht oder zum Ändern in einem Formular anzeigt. Die Frage ist, wie ich über die selektierte Zeile auf die richtigen Daten in Vector/Datenbank zugreifen kann. Ich hab im TableModel isCellEditable auf false gesetzt, weil ich die dAten nicht direkt in der Table bearbeiten möchte, sondern über Eingabeformulare.

Immerhin hab ich festgestellt, dass die mehrfache Konsolenausgabe damit zusammenhängt, dass auf mehrere Ereignisse reagiert wird, also auf Mausklicken und auch auf Mausloslassen.

hey-ho


----------



## manuche (16. Jun 2008)

Welche Java-Version benutzt du denn?
Bei 1.6 hast du z.b. im DefaultTableModel einen Konstruktor, indem du einen Vector für die Daten und einen für die Spaltennamen angeben kannst! Damit sollte das ganze doch kein Problem mehr sein, da dein Datenvector als Grundlage für die Tabelle dient und du zusätzlich angeben kannst was angezeigt werden soll!


----------



## hey-ho (16. Jun 2008)

Ja, ich hab Version 1.6.

Ich hab mir ein eigenes TableModel gebaut, welches von AbstractTableModel abgeleitet ist. Dort gibt es für die Spaltennamen ein String-Array und für die Daten hab ich einen Vector (Vector<Person>), d.h. die Personendaten werden aus der Datenbank geholt, für jeden Personeintrag ein Person-Objekt gebildet, welche dann in dem Vector<Person> gespeichert sind. Dieser Vector wird nun vom TableModel genommen, um die Daten in der Tabelle anzuzeigen. Im Objekt Person gibt es auch eine Variable für die ID aus der Datenbank.

Die selektierte Zeile gibt mir ja nur die Werte wieder, die auch in der Tabelle stehen. Die ID wollte ich aber nicht dort anzeigen. Geht es denn nur so, dass ich die Spalte mit der ID "verstecke", also nicht mit anzeige?

Vielleicht bin ich ja aber auch völlig auf dem Holzweg und es geht doch irgendwie ganz anders...


----------



## hey-ho (16. Jun 2008)

Ich hatte ja auch weiter oben geschrieben, dass nach einem Sortieren der Tabelle über Klick auf die Tabellenköpfe die Zeilennummern der Tabelle nicht mehr mit dem Index des Vectors übereinstimmen, ich also über diesen Wert nicht mehr auf den richtigen Eintrag im Vector komme.


----------



## manuche (16. Jun 2008)

Ich kenn mich jetzt nicht so mit JTables aus, aber vermute mal, dass der Vector mit sortiert wird, wenn du die Tabelle sortierst... Oder hast du das schonmal erfolglos getestet?


----------



## Michael... (16. Jun 2008)

vielleicht hilft die convert... Methode weiter:

```
int modelRow = table.convertRowIndexToModel(table.getSelectedRow());[code]
```


----------



## hey-ho (16. Jun 2008)

manuche hat gesagt.:
			
		

> Ich kenn mich jetzt nicht so mit JTables aus, aber vermute mal, dass der Vector mit sortiert wird, wenn du die Tabelle sortierst... Oder hast du das schonmal erfolglos getestet?



Nee, klappt leider nicht, weil der mit getSelectedRow() zurückgegebene int-Wert sich weiterhin an der Reihenfolge der Tabellenzeilen orientiert und diese dann beginnend mit 0 durchnummeriert. Dieser Wert wird ja dann auch für den Index im Vector genommen, was dann natürlich nicht mehr stimmt, wenn die Tabelle neu sortiert wurde.

Ich versuche es gleich mal mit dem vorgeschlagenen convertRowIndexToModel und melde mich dann wieder...

Vielen Dank schon mal für eure Hilfe.

hey-ho


----------



## hey-ho (16. Jun 2008)

Hmm, irgendwas stimmt da immer noch nicht. Seit ich das "valueChanged(ListSelectionEvent e)" vom ListSelectionListener drin habe, werden nicht mehr ganze selektierte Zeilen angezeigt, sondern nur noch einzelne Zellen. Manchmal ändert sich die Selektion auch überhaupt nicht, wenn ich in eine andere Zeile klicke. Hab das Gefühl, dass sich die beiden Einträge gegenseitig "stören":


```
[...]
getSelectionModel().addListSelectionListener(this);

setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
[...]
```

Am Anfang sieht es noch so aus, als werden wenigstens die richtigen Werte in der Konsole ausgegeben. Aber wenn ich auf einen Tabellenkopf klicke, um die Tabelle mit dieser Spalte neu zu sortieren, bekomme ich diese Fehlermeldung:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
	at javax.swing.DefaultRowSorter.convertRowIndexToModel(DefaultRowSorter.java:501)
	at javax.swing.JTable.convertRowIndexToModel(JTable.java:2564)
	at PersonenTable.valueChanged(PersonenTable.java:41)
[...]

Ich poste hier jetzt nochmal meinen aktuellen Code der JTable. Vielleicht kann ja mal wer nen Blick drüberwerfen, wer Ahnung von JTables hat und mir einen Tipp geben kann, was ich da falsch mache.


```
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;


public class PersonenTable extends JTable implements ListSelectionListener {
	
	PersonenTableModel personenTableModel;
	
	public PersonenTable(PersonenTableModel personenTableModel) {
		
		this.personenTableModel = personenTableModel;
		
		this.setModel(personenTableModel);
		
		setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		setAutoCreateRowSorter(true);
		
		getTableHeader().setReorderingAllowed(false); 
		
		getSelectionModel().addListSelectionListener(this);

	    setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

	    columnModel = getColumnModel();
	    columnModel.getColumn(0).setPreferredWidth(180);
	    columnModel.getColumn(1).setPreferredWidth(180);
	    columnModel.getColumn(2).setPreferredWidth(80);

	    DefaultTableCellRenderer rendererRight = new DefaultTableCellRenderer();
	    rendererRight.setHorizontalAlignment(SwingConstants.RIGHT);
	    columnModel.getColumn(2).setCellRenderer(rendererRight);

	}

	
	public void valueChanged(ListSelectionEvent e) {
		int row = convertRowIndexToModel(getSelectedRow());

		if (row != -1) {
		System.out.println("Ausgewählte Zeile: " + row);
		System.out.println("Daten aus der Tabelle: " + personenTableModel.getValueAt(row,1).toString()
				+ " " + personenTableModel.getValueAt(row,0).toString());
		System.out.println("Daten aus dem Vector: "
				+ personenTableModel.personen.get(row).getVorname()
				+ " " + personenTableModel.personen.get(row).getNachname());
		}

	}

}
```


----------



## hey-ho (18. Jun 2008)

OK, ich glaube ich hab's nach langem Hin- und herprobieren endlich gelöst. Scheint erstmal so einigermassen zu funktionieren. Muss ich aber bestimmt noch weiter testen und verbessern. Falls es noch andere interessieren sollte, hier der Code:

Erst mal die JTable (den SelectionListener hab ich in eine andere Klasse ausgelagert)


```
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;


public class PersonenTable extends JTable {
	
	PersonenTableModel personenTableModel;
	
	public PersonenTable(PersonenTableModel personenTableModel) {
		
		this.personenTableModel = personenTableModel;
		
		this.setModel(personenTableModel);
		
		setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		setAutoCreateRowSorter(true);
		
		getTableHeader().setReorderingAllowed(false); 
		
            SelectionListener listener = new SelectionListener(this);
	    getSelectionModel().addListSelectionListener(listener);
	    getColumnModel().getSelectionModel()
	        .addListSelectionListener(listener);

	    columnModel = getColumnModel();
	    columnModel.getColumn(0).setPreferredWidth(180);
	    columnModel.getColumn(1).setPreferredWidth(180);
	    columnModel.getColumn(2).setPreferredWidth(80);

	    DefaultTableCellRenderer rendererRight = new DefaultTableCellRenderer();
	    rendererRight.setHorizontalAlignment(SwingConstants.RIGHT);
	    columnModel.getColumn(2).setCellRenderer(rendererRight);

	}

}
```

Und hier der SelectionListener:


```
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class SelectionListener implements ListSelectionListener {
	
		PersonenTable personenTable;

        public SelectionListener(PersonenTable table) {
        	
            this.personenTable = table;
            
        }
        
 
        public void valueChanged(ListSelectionEvent e) {
    
            if (e.getValueIsAdjusting()) {
            	
            		if (personenTable.getSelectedRow() != -1) {
            			int modelRow = personenTable.convertRowIndexToModel(personenTable.getSelectedRow());
            			System.out.println("--> " + modelRow);
                        System.out.println("--> " + personenTable.personenTableModel.personen.
                        		get(modelRow).getNachname());            			
            		}

            }
        }
        

    }
```


----------



## hey-ho (18. Jun 2008)

Das Problem ist wohl zur Zeit nur noch, dass jetzt zwar wegen dem getValueIsAdjusting nur noch 1 Event beim Mouseklick ausgelöst wird, dafür aber nicht mehr auf Bewegen der Selektion über die Pfeiltasten. Na ja, das krieg ich (früher oder später) auch noch hin...


----------



## Michael... (18. Jun 2008)

dann nimm doch einfach:

```
if (!e.getValueIsAdjusting())...
```


----------



## hey-ho (18. Jun 2008)

Na ja, dann bekomme ich aber wieder zweimal die Ausgabe auf der Konsole, wenn es nicht getValueIsAdjusting ist. Vielleicht ist es a aber auch egal, ob ich meinen gewünschten Wert (ID) nun einmal oder zweimal bekomme, solange ich das nicht über die Konsole ausgeben muss...


----------



## Michael... (18. Jun 2008)

Beim MouseClick werden zwei Events ausgeführt:
1. Event getValueIsAdjusting= true
2. Event getValueIsAdjusting= false
Beim Wechsel mit der Cursortaste wird nur ein Event ausgeführt:
1. Event getValueIsAdjutsting= false

Von daher sollte, wenn man anstelle von e.getValueIsAdjusting()
!e.getValueIsAdjusting() verwendet der restliche Code in beiden Fällen nur einmal ausgeführt werden - sonst würde ich sagen stimmt da was nicht.


----------



## hey-ho (18. Jun 2008)

Stimmt! Das klappt jetzt prima. Nur beim ersten Mal, wenn noch keine Zeile selektiert war, bekomme ich die Ausgabe zweimal, ansonsten nur einmal, so wie ich es haben wollte. Auch nachdem die Tabelle neu sortiert wurde. Vielen Dank für die Hilfe...


----------

