Swing Daten in JTable aktualisieren

lowbird

Mitglied
Hallo,

ich hab folgendes Problem, ich benutze ein JTable um Daten für einen Ausflugsplaner anzuzeigen und die Teilnehmer zu verwalten. Ich habe ein eigenes TableModel von AbstractTableModel abgeleitet und dieses implementiert zusätzlich noch TableModelListener. Um festzulegen ob ein Teilnehmer an einer bestimmten Aktivität während des Ausflugs teilnimmt, existiert für jede der Aktivitäten eine eigene Spalte mit Checkboxen. In Abhänigkeit von der Anzahl der Teilnehmer je Aktivität ändert sich der Betrag, den jeder der Teilnehmer zu bezahlen hat. Nach jeder Änderung in einer Checkbox wird tableChanged(...) aufgerufen(durch modelxy.fireTableChanged(....)). Hierin werden die Kosten jedes Teilnehmers neu errechnet und in dem jeweiligen Objekt gespeichert.

Leider ändert sich der Endbetrag jedes Teilnehmers in der JTable erst nachdem ich eine weiter Checkbox angeklickt hab. Das führt dazu das die Kosten quasi immer einen Schritt zu spät korrekt angezeigt werden.

Wie kann ich innerhalb der Funktion tableChanged(..) erzwingen, dass jeweils aktuelle Daten aller Teilnehmer angezeigt werden?

modelxy.fireTableDataChanged() funktionniert leider nicht

Ich hoffe jmd kann mir helfen.
 
Prinzipiell läuft es so: Wenn sich in einem Modell etwas ändert, müssen die entsprechenden Listener benachrichtigt werden. Und das funktioniert bei Tabellen mit JTable und AbstractTableModel (bei mir zumindest) bisher immer.

Von der Beschreibung her tippe ich darauf, dass etwas in deiner Ereigniskette nicht stimmt. Wenn dein Tabellenmodell selbst Listener auf anderen Modellen ist, dann muss er z.B. nach einer (fremden) Änderung seine eigenen Daten aktualisieren und dann erst seine eigenen Listener benachrichtigen. Als Quellcode etwa so:
Java:
public class MyTableModel extends AbtractTableModel implements XYListener
{
    @Overwrite
    public void xyChanged( XYEvent event )
    {
        // das ist nur ein Beispiel, wie die eigenen Daten aktualisiert werden könnten
        myTableData[ irgendwieDieZeileRausfinden ][ spalteAbhaengigVonXY ]
             = event.getValue();

        // jetzt muss das Modell seinen eigenen Listener die Änderung weiterleiten (ua der JTable)
        this.fireTableCellUpdated(
                irgendwieDieZeileRausfinden,
                spalteAbhaengigVonXY); 
    }
}

Du kannst mit Testausgaben oder Debugger auch mal prüfen, ob dein Modell die fremden Ereignisse mitbekommt. Vielleicht hast du auch einfach vergessen es als Listener wo dranzuhängen.

Das ist eigentlich alles, was ich bisher dazu sagen kann. Wenn dein Programm nicht zu umfangreich ist, kannst du es auch mal posten, aber ich denke schon, dass es (zu)viel ist 🙂
Oder die - deiner Meinung nach - schuldigen Stellen posten.
 
Hmm also der TableModelListener in meinem TableModel ist nur ein Listener auf sich selbst.

Ich versuch mal die wesentlichen Code-Stücke zu posten

Java:
public class TeilnehmerTableModel extends AbstractTableModel implements TableModelListener
{
public TeilnehmerTableModel(Ausflug a)
{
...
this.addTableModelListener(this);	
}
...
...
...
public void setValueAt(Object arg0, int arg1, int arg2)
{
        ...
        fireTableChanged(new TableModelEvent(this, arg1, arg2));			
}
public void tableChanged(TableModelEvent arg0)
{
	//Teilnehmer je Aktivität zählen und Kosten für jeden einzelnen Teilnehmer berechnen und in 
        //den jeweiligen Objekten speichern

        //Nach allen Berechnungen soll dann das TableModel aktualisiert werden
}
 
Zuletzt bearbeitet:
Ein Model als Listener an sich selbst zu hängen... naja.
Du bekommst doch über die setValueAt(...) Änderungen an einzelnen Zellen mit.

Ich würde die Aktivitäten als eigene Objekte mit den Attributen nameActivity, numOfParticipants, price und den Methoden getActivityName(), getPricePerParticipant()
Diese Objekte würde ich in das TableModel stecken und die Werte der beiden Methoden anzeigen lassen. Wenn sich dann ein Teilnehmer einer Aktivtät anschliesst oder von einer abmeldet einfach die Teilnehmerzahl anpassen und per fire... der JTable mitteilen, dass sich was am Model geändert hat.
 
Ein Model als Listener an sich selbst zu hängen... naja.
An welches Objekt sollte ich ihn sonst hängen?
Wobei mir grad aufgefallen ist, dass der Listener sinnlos ist, geht auch ohne -.-

Du bekommst doch über die setValueAt(...) Änderungen an einzelnen Zellen mit.
Jo und dann berechne ich die Kosten für jeden Teilnehmer neu.
Das Problem ist halt, dass es Aktivitäten gibt bei denen sich die Anzahl der Teilnehmer auf die Kosten für den Einzelnen auswirkt, somit muss ich für alle Teilnehmer die Endbeträge neu berechnen.
Das ist ja auch nicht das Problem.

Ich würde die Aktivitäten als eigene Objekte mit den Attributen nameActivity, numOfParticipants, price und den Methoden getActivityName(), getPricePerParticipant()
Diese Objekte würde ich in das TableModel stecken und die Werte der beiden Methoden anzeigen lassen. Wenn sich dann ein Teilnehmer einer Aktivtät anschliesst oder von einer abmeldet einfach die Teilnehmerzahl anpassen und per fire... der JTable mitteilen, dass sich was am Model geändert hat.
Die betreffenden Objekte sind im TableModel... sonst könnte ich ja nicht die neuen Endbeträge berechnen. Diese werden auch korrekt errechnet.
Nach der Berechnung kommt dann ein "fireTableDataChanged()", also am Ende der Funktion setValueAt(...).
Das Problem ist einfach das die Änderungen nicht sofort in der Tabelle erscheinen, sondern erst nach der nächsten Aktion in der Tabelle(Checkbox-Klick).
 
Hier mal ein Beispiel wie ich es gemeint habe.
Ist nur ein erster Schnellschuss ohne grossartige Überlegung, aber vielleicht liefert es Dir ja einen möglichen Ansatz:
Java:
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

public class ActivityTableDemo extends JFrame {
	
	public ActivityTableDemo() {
		Activity sailing = new Activity("Sailing", 10000d) {
			public double getPricePerPerson() {
				if (participants==0)
					return 0d;
				return price/participants;
			}
		};
		Activity climbing = new Activity("Climbing", 600d) {
			public double getTotalPrice() {
				return price * participants;
			}
			
			public double getPricePerPerson() {
				if (participants==0)
					return 0d;
				return price;
			}
		};
		ActivityTableModel model = new ActivityTableModel();
		model.setDataVector(new Object[][]{
				{"Meier", sailing, null, false, null},
				{"Meier", climbing, null, false, null},
				{"Huber", sailing, null, false, null},
				{"Huber", climbing, null, false, null},
				{"Schmidt", sailing, null, false, null},
				{"Schmidt", climbing, null, false, null}
				},
				new String[] {"Person", "Aktivität", "Gesamtpreis", "Teilnahme", "Preis p.P."});
		JTable table = new JTable(model);
		this.getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
	}
	
	class ActivityTableModel extends DefaultTableModel {
		public boolean isCellEditable(int row, int column) {
			if (column==3)
				return true;
			return false;
		}
		
		public Class<?> getColumnClass(int column) {
			if (column==2)
				return Double.class;
			if (column==3)
				return Boolean.class;
			if (column==4)
				return Double.class;
			return Object.class;
		}
		
		public Object getValueAt(int row, int column) {
			Activity activity = (Activity)super.getValueAt(row, 1);
			switch (column) {
			case 1: return activity.getTitle();
			case 2: return activity.getTotalPrice();
			case 4: {
				boolean checked = ((Boolean)super.getValueAt(row, 3)).booleanValue();
				if (checked)
					return activity.getPricePerPerson();
				else
					return 0d;
			}
			}
			return super.getValueAt(row, column);
		}
		
		public void setValueAt(Object obj, int row, int column) {
			if (column==3) {
				boolean checked = ((Boolean)obj).booleanValue();
				Activity activity = (Activity)super.getValueAt(row, 1);
				if (checked)
					activity.addParticipant();
				else
					activity.removeParticipant();
			}
			super.setValueAt(obj, row, column);
			this.fireTableDataChanged();
		}
	}
	
	abstract class Activity {
		protected String title;
		protected double price;
		protected int participants;
		
		public Activity(String title, double price) {
			this.title = title;
			this.price = price;
			this.participants = 0;
		}
		
		public String getTitle() {
			return title;
		}
		
		public double getTotalPrice() {
			return price;
		}
		
		public void addParticipant() {
			this.participants++;
		}
		
		public void removeParticipant() {
			this.participants--;
		}
		
		public abstract double getPricePerPerson();
	}
	
	public static void main(String[] args) {
		JFrame frame = new ActivityTableDemo();
		frame.setBounds(0, 0, 500, 300);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
}
 

Zurück
Oben