# Problem mit JTable und fireTableDataChanged()



## Weisswurst (4. Jan 2008)

Hi!

Ich habe ein kleines Problem mit meiner Tabelle.

In meiner Klasse wird zu oberst ein DataModel initialisiert und an eine Tabelle übergeben.
Die GUI wird aufgebaut und dann eine Funktion aufgerufen, die das DataModel füllt.

DataModel ist übrigens ne Ableitung von AbstractTableModel,

Das GAnze sieht in etwa so aus:


```
public class meineKlasse()
{
    DataModel dataModel = new DataModel();
    JTable table;
    JScrollPane scrollPane;

    public go()
    {
        table = new JTable(dataModel);
         //Gui zusammenstecken, table in Scrollpane stecken...
        updateDataModel() //Einlesen der Daten in das DataModel
    }

    public updateDataModel()
    {
        dataModel = new DataModel(); //Wieso???
        //Mehrere Funktionen, die Daten über die addDataSet() Methode in das dataModel schreiben
        table.setModel(dataModel); //Eigentlich sollte aktualisiert werden nicht komplett neu gemacht werden...
    }
}
```

So die Änderungen im dataModel werden über eine Funktion namens addDataSet() durchgeführt, die eine Klasse entgegennimmt, in der einige Werte drin stehen, und diese in eine ArrayListe steckt. So ein DataSet ist quasi eine Zeile in meiner Tabelle und die ArrayList sammelt diese Zeilen. 


```
public class DataModel()
{
    public void addDataSet(DataSet set)
    {
        dataRow.add(set); //DataRow ist das ArrayList
        fireTableDataChanged();
    }
}
```

Mein Problem ist nun, dass beim Einlesen der Daten in das DataModel Dateien von versch. Rechenrn im Netz abgefragt werden müssen. Das dauert ne Weile. Ich hätte gerne, dass die Tabelle aktuell vorhandenen DataSets anzeigt, während neue eingelesen werden. 

Wenn ich aber in der UpdateDataModel Methode ein neues DataModel anlege und dann sogar noch neu der Tabelle zuweisen muss, wird des nix mit der ständigen Aktualisierung. Das ich ein neues DataModel neu der Tabelle zuweisen muss ist klar. Aber warum muss ich ein neues DataModell anlegen???

Die Aktualisierung muss übrigens in der Funktion bleiben, weil es da so nen Refreshbutton gibt, der wie der Name schon sagt, die Tabelle refreshen soll...

Sicher ne Anfänger Frage und für eich Cracks gar kein Problem 
Vielen Dank!!!


----------



## anfänger15 (4. Jan 2008)

du könntest mit removeRow und addRow die Datensätze die sich geändert haben in der Tabelle ändern. Dadurch bleiben alle anderen Datensätze da weil das TableModel nicht gelöscht wird.

vllt. gibt es aber noch besser Möglichkeiten


----------



## Weisswurst (4. Jan 2008)

Hmm, ich versteh nicht ganz, wie mir das Helfen soll.

Im Prinzip ist es schon richtig eine komplett neue Tabelle zu zeichnen, weil sich im Zweifel alle Zeilen 
geändert haben.

Ich hatte extra dafür eine resetList Funktion, die das Modell geleert hat.

Meine eigentliche Frage ist aber, warum ich in der UpdateFunktion exta nochmal ein dataModel erstellen muss.
Eigentlich habe ich doch schon eins für die Klasse, das halt von mehreren Funktionen vearbeitet wird.


----------



## HLX (4. Jan 2008)

Versuchs mal so:


```
public class meineKlasse()
{
    JTable table;
    JScrollPane scrollPane;

    public void initialize()
    {
        table = new JTable(dataModel);
        DataModel dataModel = new DataModel();
        table.setModel(dataModel); // Model einmalig zuweisen
        //Gui zusammenstecken, table in Scrollpane stecken...
    }

    public void go() {
          updateDataModel() //Einlesen der Daten in das DataModel
    }

    public void updateDataModel()
    {
           //Mehrere Funktionen, die Daten über die addDataSet() Methode in das dataModel schreiben
           // Zugriff über table.getModel()
    }
}
```


----------



## Weisswurst (4. Jan 2008)

Ich habe jetzt mal ein paar Ausgaben eingebaut.
Ich musste dabei feststellen, dass die Tabelle gar nicht neugezeichnet wird wenn geupdatet wird.
Die Funktionen getColumnCount und RowCount und ColumnName werden trotz fireTableDataUpdated und fireTableStructureChanged nicht erneut aufgerufen.

Ich hänge mal das ganze Modell an...

```
import java.io.Console;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;

public class DataModel extends AbstractTableModel implements Iterator<DataSet>
{
	/**
	 * 
	 */
	private static final long	serialVersionUID	= 1L;
	
	private ArrayList<DataSet> dataRow = new ArrayList<DataSet>();
	private Vector<TableModelListener> listeners = new Vector<TableModelListener>();
	private int iMaxCountOfUsers = 0;
	private Iterator<DataSet> iterator;
	
	public void update()
	{
		System.out.println("Update requested");
		fireTableStructureChanged(); 
		fireTableDataChanged();
	}
	
	public void addDataSet(DataSet set)
	{
		dataRow.add(set);
		System.out.println("Neuer Datensatz: " + set.getNameServer() + " User Count: " +  set.getLoggedInUserList().size());
		if(set.getLoggedInUserList().size() > iMaxCountOfUsers)
			iMaxCountOfUsers = set.getLoggedInUserList().size();
		 iterator = dataRow.iterator(); //Der Iterator muss immer aktualisiert werden wenn neues Element...
	}
	
	public void addTableModelListener(TableModelListener arg0)
	{
		listeners.add(arg0);
	}
	
	public Class<?> getColumnClass(int col)
	{
		switch (col)
		{
		case 0:
			return String.class;
		case 1:
			return String.class;
		case 2:
			return String.class;
		case 3:
			return String.class;
		case 4:
			return Integer.class;
		default:
			return String.class;
		}
		
	}
	
	public String getColumnName(int arg0)
	{
		System.out.println("GetColumnName: " + arg0);
		switch (arg0)
		{
		case 0:
			return "IP Server";
		case 1:
			return "Name Server";
		case 2:
			return "Project";
		case 3:
			return "Version";
		case 4:
			return "UserAnzahl";
		default:
			return "Active User";
		}
	}
	
	public int getColumnCount()
	{	
		System.out.println("GetColumnCount: " + (5 + iMaxCountOfUsers));
		return 5 + iMaxCountOfUsers;
	}

	public int getRowCount()
	{
		System.out.println("GetRowCount: " + (dataRow.size()));
		return dataRow.size();
	}

	public Object getValueAt(int row, int col)
	{
		DataSet set = dataRow.get(row);
		
		switch (col)
		{
			case 0:
				return set.getIPServer();
			case 1:
				return set.getNameServer();
			case 2:
				return set.getProject();
			case 3:
				return set.getVersion();
			case 4:
				return set.getUserCount();
			default:
			{
				try
				{
					return set.getLoggedInUserList().get(col - 4);
				} catch (RuntimeException e)
				{
					e.printStackTrace();
				}
			}
			return null;
		
		}
	}
	public boolean isCellEditable(int arg0, int arg1)
	{

		return false;
	}

	public void removeTableModelListener(TableModelListener arg0)
	{
		listeners.remove(arg0);		
	}
	
	public void resetList()
	{
		Collections.fill(dataRow, new DataSet());
		update();
	}

	public void setValueAt(Object arg0, int arg1, int arg2)
	{
		return;	
	}

	public boolean hasNext()
	{
		return iterator.hasNext();
		
	}

	public DataSet next()
	{
		return iterator.next();
	}

	public void remove()
	{
		iterator.remove();
		update();
	}
}
```


----------



## HLX (4. Jan 2008)

Hast du außerdem o.g. Änderung eingebaut?


----------



## Weisswurst (4. Jan 2008)

Ja, aber es hilft nicht.
Das Updateverhalten ist im wesentlichen identisch 

Edit:
Die letzte Zeile aus meinem log besagt "Update requested". Also wurde die update Funktion aus dem Model aufgerufen. (siehe oben) Die müsste eigentlich die Tabelle komplett zum neuzeichen veranlassen...
Aber die ganzen getFunktionen aus dem Model werden nicht mehr aufgerufen.

Edit2:
Ich habe sicherheitshalber nochmal nachgeschaut. An der stelle wo dieses letzte "Update requested" ausgegeben wird, ist das DataModel reichlich mit eigentlich anzuzeigenden Daten gefüllt...


----------



## HLX (4. Jan 2008)

Wo wird die Methode 'addTableModelListener' aufgerufen?


----------



## Weisswurst (4. Jan 2008)

Von mir selbst niergends.
Sollte?

Edit:
Die Funktion wird aber ganz zu Beginn einmal aufgerufen.

Edit2:
Der Listener wird automatisch hinzugefügt, wenn das Model an die Table übergeben wird.
Also bei JTable table = new JTable(dataModel);


----------



## Weisswurst (4. Jan 2008)

Sieht so aus, als hätte ich es.
In der update Funktion ein TableModelEvent anzulegen und an den listener zu übergeben tut dem Updateverhalten sehr gut.


----------



## André Uhres (4. Jan 2008)

Warum so kompliziert? Lass die listeners einfach weg, die sind doch schon in der Oberklasse vollständig implementiert.


----------



## Weisswurst (15. Jan 2008)

So, ich komm endlich mal dazu an dieser Front weiter zu machen.

Das mit den Listenern habe ich so gemacht, weil ich durch ein Tut den Einduck bekommen habe, dass das so gemacht werden müsste. Aber bei dem Tut ging es dann auch noch darum die Zellen mit einem eigenen Renderer zu versehen.
Möglicherweise sind deshalb die Listener dabei.

Ich habe sie jetzt raus genommen.

Das Programm läuft jetzt im Prinzip. Leider werden jede menge Exceptions geschmissen, weil ein Array out of Bounds ist.
Ich suche jetzt schon eine ganze Weile rum und kann mit Sicherheit sagen, dass der Fehler bei "fireTableStructureChanged();" Auftritt. 

Weiterhin kann ich noch sagen, dass im defaultTableColumnModel die Funktion getColumn() aufgerufen und direkt danach in util.vector.elementAt() die Exception fliegt. 

in meinem DataModel gibt es die Funktion 


```
public int getColumnCount()
   {   
      System.out.println("GetColumnCount: " + (5 + iMaxCountOfUsers));
      return 5 + iMaxCountOfUsers;
   }
```

Ich habe das exemplarisch mehrfach durchgezählt und bin mir doch sehr sicher, dass der Wert "iMaxCountOfUsers" stimmt. Das ich tatsächlich 5 zusätzliche Spalten habe sieht man ja im Quellcode ein paar Posts weiter oben.

Das Programm funktioniert zwar so, aber mit den ganzen Exceptions möcht ich's nicht liegen lassen


----------



## André Uhres (15. Jan 2008)

Weisswurst hat gesagt.:
			
		

> ..kann mit Sicherheit sagen, dass der Fehler bei "fireTableStructureChanged();" Auftritt..


Machst du "fireTableStructureChanged" auch genau dort, wo die Struktur verändert wird?
Wenn ich das richtig sehe, dann müsste hier z.B. ein "fireTableStructureChanged" hin:

```
public void addDataSet(DataSet set)
   {
      dataRow.add(set);
      System.out.println("Neuer Datensatz: " + set.getNameServer() + " User Count: " +  set.getLoggedInUserList().size());
      if(set.getLoggedInUserList().size() > iMaxCountOfUsers)
         iMaxCountOfUsers = set.getLoggedInUserList().size();
       iterator = dataRow.iterator(); //Der Iterator muss immer aktualisiert werden wenn neues Element...
   }
```


----------



## Guest (15. Jan 2008)

AHA! Wenn ich alles Sets eintragen lasse und erst dann mein Update mit fire dies und das mache, dann funzt das.
Offensichtlich habe ich update ich da irgendwann wenn die Daten nicht passen.

Ich schau mir das nochmal genau an.


----------

