Swing AbstractTableModel - Observer..

OlliL

Bekanntes Mitglied
Hallo,

folgende Problemstellung:

Ich habe ein Model welches Observable ist, und eine View welche der Observer ist.
Auf der View wird eine Tabelle mit einem AbstractTableModel dargestellt.
Das AbstractTableModel instanziert für jede Zeile ein neues Model.
Nun kann ich innerhalb des AbstractTableModels aber nicht die View als Observer hinzufügen.
Mein Model nimmt aber eine Prüfung der gerade eingegebenen Daten vor und soll eine Rueckmeldung an die View geben bei einer Fehleingabe um den Fehler in einem standard Label anzuzeigen.

Die View ist natürlich nicht Observer all meiner Model-Instanzen (pro Zeile eine Instanz), somit kommt das notifyObservers aus meinem Modeli natürlich nicht in der View an.

Wie löse ich das Problem am besten?
 

OlliL

Bekanntes Mitglied
Jo, den Oracle Artikel kenn ich.
Ich habe eine Eingabemaske welche mit einer Zeile startet und sich autom. um eine Leerzeile erweitert wenn Daten in die aktuelle Leerzeile eingegeben wird. Orientiert habe ich mich dabei an:

A Simple Interactive JTable Tutorial

Java:
 public class InteractiveTableModel extends AbstractTableModel {
...
    public void setValueAt(Object value, int row, int column) {
         AudioRecord record = (AudioRecord)dataVector.get(row);
         switch (column) {
             case TITLE_INDEX:
                record.setTitle((String)value);
                break;
             case ARTIST_INDEX:
                record.setArtist((String)value);
                break;
             case ALBUM_INDEX:
                record.setAlbum((String)value);
                break;
             default:
                System.out.println("invalid index");
         }
         fireTableCellUpdated(row, column);
     }
...
     public void addEmptyRow() {
         dataVector.add(new AudioRecord());
         fireTableRowsInserted(
            dataVector.size() - 1,
            dataVector.size() - 1);
     }

Dadurch wird (angenommen AudioRecord ist mein Model) immer ein neues Model pro Zeile instanziert.

Wo sollte ich denn die Validierung der eingegebenen Werte vornehmen? Um bei dem Beispiel zu bleiben habe ich das bisher in den set-Prozeduren von AudioRecord gemacht, nur kann ich von dort halt nicht die View "notifyen".
 
Zuletzt bearbeitet:

Michael...

Top Contributor
AudioRecord hört sich nach einem Datensatz an und sollte nicht das Model der View sein. Du solltest ein AudioModel verwenden und darin die Datensätze verwalten, dann hast Du auch nur ein Model für die View. Evtl. kann man ja direkt das TableModel nutzen.
 

OlliL

Bekanntes Mitglied
Ok,, ich sollte also eher ein Model der View machen, welches einen Vector aller Records abbildet?
Wie validiere ich dann am besten die Record-Felder so das ich bei einem Fehler eine Meldung in der View anzeigen kann?
 
C

Camino

Gast
Was für Fehler meinst du denn, die bei der Eingabe passieren könnten und überprüft werden müssten? Eine Überprüfung der Eingaben würde ich bei der GUI oder einem Controller stattfinden lassen, beim Klick auf "Speichern", also bevor die Daten an das Model weitergegeben werden.

Und normalerweise hast du 1 Model für die Tabelle (TableModel), in dem z.B. in einer ArrayList deine Objekte (Datensätze) gesammelt sind und über das TableModel in der Tabelle angezeigt werden.
 

OlliL

Bekanntes Mitglied
Nun - NotizBuch enthaellt keine JTable, oder? Generell verwende ich Observer/Observable ohne Probleme. Nur im Zusammenspiel mit einer JTable wo halt mein Record-Model die Validierung der Daten vornimmt habe ich ein Problem, da dieses keinen direkten Bezug zur View hat auf der die Tabelle dargestellt wird.

Und Validierung nach dem Klick auf Speichern - klar geht das, aber warum sollte man sowas machen? Von der Usability her ist es wesentlich sinnvoller direkt Fehler auszuwerten. Warum soll ich 20 mal ein Datum in einem falschen Format eingeben, wenn ich es direkt nach der ersten Eingabe anzeigen kann? Ja, schlechte Beispiel, Datenformate könnte man auch autom. erkennen und "korrigieren". Es ist auch nur ein Beispiel. "Erstmal alles eingeben und dann die 30 Fehler anzeigen" finde ich nur bedingt sinnvoll und will es daher nicht so machen "nur weil es einfacher" ist ;)

Wie wäre folgende Lösung:

1. Es gibt ein Model "RecordModel" mit Getter und Setter Funktionen
2. Es gibt ein Model "ViewModel" welches eine Liste von "RecordModel"-Objekten enthällt. Getter und Setter Funktionen existieren welche die Validierung vornehmen und dann die Getter/Setter-Funktionen aus "RecordModel" aufrufen.
3. Das Model "ViewModel" ist Observable
4. Es gibt ein "TableModel" welches AbstractTableModel erweitert. Der Constructor von "TableModel" erwartet als Parameter "ViewModel" und legt diesen intern ab. getValueAt und setValueAt rufen dann die Getter und Setter Funktionen aus "ViewModel" auf und übergeben immer die row mit (Index der Liste von "TableRecord"s)
5. "ViewModel" und "View" werden instanziert. View wird als Observer von ViewModel bekannt gemacht
6. Mein Controller instanziert "TableModel" und übergibt das vorher instanzierte "ViewModel"

Nun sollte ich aus "ViewModel" Events auslösen können. Aber - ich finde es unsauber mein "ViewModel" in mein "TableModel" (AbstractTableModel) zu parken und auf diesem zu arbeiten. Besser wäre es glaube ich, wenn mein "ViewModel" ein Observer von meinem "TableModel" wäre und Anderungen mitbekommt und meinetwegen "RecordModel" zur Ablage übermittelt bekommt. Nur - dann habe ich das Problem der Validierung und Fehleranzeige immer noch nicht erledigt.

Heute sieht es so aus:
Code:
+------------+       +-----------+                              +-----------+
|            +-------+ ViewModel |                              +RecordModel|
|            |       +----+------+                            / +-----------+
| Controller |           (|) Observer                       / 
|            |       +----+------+       +-----------+    /     +-----------+
|            +-------+   View    +-------+ TableModel+---+------+RecordModel|
+------------+       +-----------+       +-----------+    \     +-----------+
                                                            \
                                                              \ +-----------+
                                                                +RecordModel|
                                                                +-----------+

Und RecordModel validiert die Daten direkt nach Eingabe, aber View sollte Fehler anzeigen.
 
Zuletzt bearbeitet:

bERt0r

Top Contributor
Dafür aber eine List. Ein List Model und dein Tablemodel sind sich auch ziemlich ähnlich. Ausserdem ist die Struktur nach dem gleichen Schema wie dein Programm aufgebaut. Aber wenn du's besser weist, ok.
 

OlliL

Bekanntes Mitglied
Besser wissen? Ich stelle nicht fest sondern frage und versuche zu verstehen!

Ich sehe halt nicht den Zusammenhang zwischen NotizBuchView welche Instanzen von NotizView enthällt welche beide Ihr eigenes Model haben und jeweils eine Observer-Beziehung zueinander haben und meinem Problem.

Ich möchte innerhalb meiner TableModel-Änderungen ein Event auslösen um Daten in meiner View anzuzeigen. Um es mit deinem Notizbuch zu vergleichen, wäre es so, als wenn du ein Event in NotizModel auslöst um in NotizBuchView etwas anzuzeigen. Bei dir ist aber nur NotizView der Observer von NotizModel.

Irgendwie habe ich ein Problem "AbstractTableModel" in das MVC Konzept richtig einzuordnen. Es scheint mehr eine Art Controller zu sein, als ein wirkliches Model.
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Hab's nur halb überflogen, aber ... hat dein "RecordModel" irgendeinen Notifikationsmechanismus? Also, gibt es auch einen "RecordListener", und gibt es irgendwelche Stellen außerhalb der Tabelle, wo jemand
- auf Änderungen am RecordModel hört (also ein RecordListener ist) oder
- Änderungen am RecordModel vornimmt (die dann in der Tabelle sichtbar werden sollen)
? (Vermutlich erstmal nicht, weil die RecordModels ja anscheinend NUR in der Table liegen, weil sie auch dort erzeugt werden und nicht nach außen sichtbar sind...?)

(Deswegen frage ich mich gerade, welche Rolle das RecordModel überhaupt einnimmt - also, wozu es überhaupt gut ist...?!)

Aber falls es RecordListeners gibt, könnte man ja ganz strikt getrennt das AbstractTableModel um die RecordModels wickeln, und das AbstractTableModel auch gleich als RecordListener an den Record hängen. Dann würde bei jedem 'recordChanged'-Event das AbstractTableModel informiert, das dann entsprechend einen TableChanged-Event erzeugen könnte.

(Aber vielleicht ist das gar nicht nötig - je nach Rolle des RecordModels....)
 

OlliL

Bekanntes Mitglied
Das RecordModel hat keinen eigenen Listener. Ich baue das gerade wie folgt um:

Das TableModel bekommt eine Referenz auf das ViewModel.
Das ViewModel verwaltet nun eine Liste von BusinessObjekten welche quasi aus jeder Tabellenzeile resultieren. Es gibt kein eigenes RecordModel mehr. Zur Verwaltung werden die Eingaben validiert und ggf. weitere Daten hinzugelesen (via DAO) um dann zu einem vollständigen Business-Objekt zu kommen welches in einer ArrayList verwaltet wird was quasi die Tabelle widerspiegelt.

Somit wird:
- das View Model instanziert
- das Table Model instanziert mit einer Referenz auf das View Model (via Controller)
- die View instanziert mit einer Referenz auf das Tabel Model.
- Das Table Model "unterhällt" sich mit dem View Model direkt
- Das View Model gibt "Meldungen" via Observer Pattern an die View weiter.

sollte so aussehen:

Code:
+------------+       +-----------+          +-----------+
|            +-------+   View    +         /+BusinessObj|
|            |       +-----------+        / +-----------+
|            |        (|)Observer        /
|            |       +-----------+      /   +-----------+
| Controller +-------+ ViewModel +-----+----+BusinessObj|
|            |       +----+------+      \   +-----------+
|            |            | 	         \
|            |       +----+------+        \ +-----------+
|            +-------+ TableModel|         \+BusinessObj|
+------------+       +-----------+          +-----------+

zwar nicht mehr so lose gekoppelt wie ursprünglicht mal gewünscht, aber macht so glaube ich mehr Sinn als vorher.
 

bERt0r

Top Contributor
Naja, es gibt schon einen Grund warum das Pattern MVC und nicht MMMMVVC heißt. Du hast einen Controller, der so aussieht als wäre er die Gottklasse die alles regelt. Ein Controller sollte aber eigentlich nur der Vermittler zwischen View und Model sein. Warum du ein "ViewModel" hast, verstehe ich auch nicht ganz.
Nochmal, in meinem Programm siehst du ein Beispiel wo Models in einem anderem Model liegen. Das is natürlich nicht exakt das was du da machen willst, für die Architektur aber relevant. Ich dachte wir haben die Sache mit Event werfen schon in einem anderen Thread gehabt, falls es da noch unklarheiten gibt mein ich...
 

OlliL

Bekanntes Mitglied
Mein Controller macht nichts ausser die "save Action" der View abzuarbeiten und die Referenzen auf diese 3 im Bild gezeigten Objekte zu halten.

mein Controller code:
Java:
public class NewMoneyFlowController implements ActionListener {

	private NewMoneyFlowModel model;
	private NewMoneyFlowTableModel tableModel;
	private NewMoneyFlowView view;
	private MenuHandler event;
	private User user;

	public void actionPerformed(ActionEvent e) {

		int numRows = model.getRecordSize();
		for(int row = 0 ; row < numRows ; row ++) {
			System.out.println(model.getMoneyFlow(row).getAmount() + "#"
					+ model.getMoneyFlow(row).getBookingDate() + "#"
					+ model.getMoneyFlow(row).getCapitalSource().getComment()
					+ "#" + model.getMoneyFlow(row).getComment() + "#"
					+ model.getMoneyFlow(row).getContractPartner().getName()
					+ "#" + model.getMoneyFlow(row).getInvoiceDate() + "#");
		}
	}

	public void addModel(NewMoneyFlowModel m) {
		this.model = m;
	}

	public void addTableModel(NewMoneyFlowTableModel tableModel) {
		this.tableModel = tableModel;
	}

	public void addView(NewMoneyFlowView v) {
		this.view = v;
	}

	public void addEvent(MenuHandler e) {
		this.event = e;
	}

	public void addUser(User u) {
		this.user = u;
	}
}

Gesteuert wird das ganze ausserhalb:

Java:
public class MenuListenerImpl implements MenuListener {

	LogonModel logonModel;
	LogonView logonView;
	LogonController logonController;

	MainModel mainModel;
	MainView mainView;
	MainController mainController;

	PanelInterface panelView;
	
	TextDaoImpl text = new TextDaoImpl();

	public void handleMyEventClassEvent(EventObject e, MenuHandler handler,
			String s, User user) {
...		
		} else if (s.equals(MenuConstants.NEW_MONEYFLOW)) {

			NewMoneyFlowController controller = new NewMoneyFlowController();
			NewMoneyFlowModel model = new NewMoneyFlowModel(user);
			NewMoneyFlowTableModel tableModel = new NewMoneyFlowTableModel(user, model);
			NewMoneyFlowView view = new NewMoneyFlowView(user, tableModel);

			model.addObserver(view);

			controller.addModel(model);
			controller.addTableModel(tableModel);
			controller.addView(view);
			controller.addEvent(handler);
			controller.addUser(user);

			view.addController(controller);

			mainController.switchPanel(view);
 

bERt0r

Top Contributor
Dann versteh ich nicht wieso dein Controller all die Referenzen hat. Bis auf sein Model braucht er doch nix kennen...
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
Zeppi Swing AbstractTableModel AWT, Swing, JavaFX & SWT 9
N Swing Print JTable mit AbstractTableModel AWT, Swing, JavaFX & SWT 1
N JTable, AbstractTableModel aus Datenbank initialisieren AWT, Swing, JavaFX & SWT 8
I JTable,AbstractTableModel Problem AWT, Swing, JavaFX & SWT 3
I JTable und AbstractTableModel AWT, Swing, JavaFX & SWT 10
H JTable - DefaultTableModel vs AbstractTableModel AWT, Swing, JavaFX & SWT 5
K Swing jTable und AbstractTableModel AWT, Swing, JavaFX & SWT 6
Ollek Swing JTable - Zeile löschen mit AbstractTableModel AWT, Swing, JavaFX & SWT 20
B Swing AbstractTableModel rows verschieben AWT, Swing, JavaFX & SWT 2
M Swing JComboBox im AbstractTableModel AWT, Swing, JavaFX & SWT 8
H Markierte Zeile löschen AbstractTableModel AWT, Swing, JavaFX & SWT 10
K JTable & AbstractTableModel AWT, Swing, JavaFX & SWT 9
G Problem mit AbstractTableModel und JComboBox AWT, Swing, JavaFX & SWT 8
P Eigenes TableModel abgeleitet von AbstractTableModel (neue rows nicht sichtbar) AWT, Swing, JavaFX & SWT 6
Lony AbstractTableModel Exception in thread "AWT-EventQueue- AWT, Swing, JavaFX & SWT 3
G AbstractTableModel kopieren AWT, Swing, JavaFX & SWT 4
G JTable (AbstractTableModel) wird nicht aktualisiert AWT, Swing, JavaFX & SWT 2
F AbstractTableModel + Vector - Wie Zeile hinzufügen AWT, Swing, JavaFX & SWT 4
D JTable, DefaultTableCellRenderer & AbstractTableModel AWT, Swing, JavaFX & SWT 11
N JTable repaint mit AbstractTableModel AWT, Swing, JavaFX & SWT 2
B jTabel, MVC und AbstractTableModel AWT, Swing, JavaFX & SWT 6
M Wann AbstractTableModel und wann DefaultTableModel AWT, Swing, JavaFX & SWT 4
S AbstractTableModel AWT, Swing, JavaFX & SWT 16
B Observer Pattern JLabel ändern AWT, Swing, JavaFX & SWT 7
U JAVAFX observer und threads AWT, Swing, JavaFX & SWT 1
R MVC Observer While-Schleife AWT, Swing, JavaFX & SWT 3
L JavaFX Java FX Anwendung nach MVC mit Observer Pattern AWT, Swing, JavaFX & SWT 15
F JavaFX MVC und Observer AWT, Swing, JavaFX & SWT 4
N Observer: update ruft nicht repaint auf AWT, Swing, JavaFX & SWT 0
K Swing und MVC + Observer AWT, Swing, JavaFX & SWT 4
G JTree - Observer oder Listener? AWT, Swing, JavaFX & SWT 6
D ComboBox-Model mit Observer AWT, Swing, JavaFX & SWT 3
D Event in Fenster ausgeben + Observer + kein Plan AWT, Swing, JavaFX & SWT 2
Rudolf AWT Observer mit AWT AWT, Swing, JavaFX & SWT 5
P Observer und GUI Update AWT, Swing, JavaFX & SWT 2
S Observer Problem AWT, Swing, JavaFX & SWT 2
D Wie mehrere Grafiken auf JPanel anzeigen lassen? (Observer pattern?) AWT, Swing, JavaFX & SWT 24
hdi Observer-Pattern bei JPanel nicht möglich? AWT, Swing, JavaFX & SWT 7
G eigenen Observer schreiben AWT, Swing, JavaFX & SWT 6
F Observer vs Listener AWT, Swing, JavaFX & SWT 2
F Eigener Observer? AWT, Swing, JavaFX & SWT 4
Z Observer/Observable und SWT AWT, Swing, JavaFX & SWT 2

Ähnliche Java Themen


Oben