eigene Klasse in JTable ablegen

Status
Nicht offen für weitere Antworten.

MrMilti

Mitglied
Hallo allerseits,

ich bastle derzeit an einem kleinen Programm, bei dem ich nun Objekte von einer mir erstellten Klasse in den Zellen einer JTable ablegen möchte und dann mit einem eigenen CellRenderer darstellen lasse.

Leider scheitere ich bereits beim Ablegen in der Tabelle, oder aber beim wieder beim Lesen aus der Zelle, da bin ich mir nicht sicher.
Java:
class MeineKlasse
	{
	protected int identity;
	public MeineKlasse() {this.identity=0;}
	}

...

protected JTable Table;
protected DefaultTableModel model = new DefaultTableModel();
protected MeineKlasse pufferelement = new MeineKlasse();

model.addColumn("");
model.addColumn("Element");
model.addColumn("Kommentar");
Table = new JTable(model);

...

model.setValueAt(pufferelement,Table.getSelectedRow(),1);

...

pufferelement=(MeineKlasse)model.getValueAt(Table.getSelectedRow(),1);
Den CellRenderer hab ich bisher noch nicht definiert, deswegen wird in der Tabellen Zelle bisher nur Klassenbezeichner und Speicheradresse (denk ich zumindest) angezeigt, aber das ist mir momentan auch nicht weiter wichtig. Was mir Kopfzerbrechen bereitet ist, dass entweder das speichern oder das laden einfach nicht funktioniert. Wenn ich also mehrere Objekte meiner Klasse in der Tabelle ablege und dann versuche, eines davon wieder aus der Tabelle zu entnehmen, werden die Werte nicht ins pufferelement übernommen.

Mir ist natürlich klar, dass ein einfacher Cast von Object auf meine Klasse ziemlicher Humbug ist, aber das war zumindest das erste, was ich wieder ohne Fehlermeldung kompilieren konnte.

Daher meine Frage(n):
- Kann man nur Objekte vom Typ Object in einer Zelle ablegen?
- Falls ja, wie kann ich die Schnittstelle zwischen diesen beiden Klassen verwirklichen?
- Falls nein, wie definiere ich den Typ, den man in einer Spalte ablegt?

Liebe Grüße und hoffentlich nicht zu großes Kopfzerbrechen,
MrMilti
 
Zuletzt bearbeitet:
Kleiner Nachtrag noch, ich hab das jetzt etwas erweitert:

Java:
class MeineKlasse extends JComponent
	{
	protected int identity;
	public MeineKlasse()
		{this.identity=0;}
	protected String paramString()
		{ return "Das ist ein Element vom Typ "+this.identity; }
	}
Was zumindest das Problem der Anzeige in der Tabelle eine Spur verbessert...
Da wird jetzt in der Tabellenzelle "MeineKlasse[Das ist ein Element vom Typ 0]" angezeigt, also schon beinahe das, was ich gern hätte 😀

Im Übrigen kann ich damit definitiv sagen, dass das Speichern in der Tabelle funktioniert, da die identity der Klasse korrekt in der Zelle angezeigt wird, nur die "Arraydarstellung" in dem Text würd ich gerne noch loswerden, falls einer von euch Rat weiß 🙂
 
Ich würde dir dazu raten, ein eigenes TableModel (abgeleitet von AbstractTableModel) zu schreiben, das auf deine Klasse zugeschnitten ist. Die Klasse aus "Anzeigegründen" von JComponent erben zu lassen finde ich grauenhaft. Schreibe dir lieber einen entsprechenden CellRenderer.

Für bessere Hilfe bräuchten wir mehr Details: Willst du eine ganze Zeile pro Objekt, oder ein Objekt in jede Zelle? Wie soll die Darstellung aussehen (nur irgendwelche Strings, oder willst du richtig "malen") usw.
 
Was ich genau vorhabe:

Am einfachsten könnte man es sich wohl vorstellen wie ein CAD-Programm. Das Objekt dieser Klasse kann dann entweder ein Punkt, eine Gerade, ein Kreisbogen und für meinen Fall noch speziellere Elemente sein. Also bearbeite ich "lokal" ein Objekt, definiere dessen Typ und Parameter und wenn ich mit diesem einen Element "zufrieden" bin, lege ich dieses in der Tabelle ab. Heraus soll dabei eine Liste von diesen verschiedenen Zeichenelementen kommen, wobei es vollkommen reicht und auch erwünscht ist, dieses Element einfach als String darzustellen.

Also da soll dann beispielsweise stehen "Punkt: X-Koordinate, Y-Koordinate, Z-Koordinate" oder "Gerade: Länge", "Kreisbogen: Bogenlänge, Öffnungswinkel" usw.

Das Objekt selbst soll dann in der zweiten Spalte liegen, Spalte 1 und 3 benötige ich rein für Textstrings.

Ich habe dieses Programm schon einmal mit c++ und gtkmm geschrieben, nun kam von meinem Auftraggeber aber die Anforderung, das online über ein Webinterface bedienen zu können, weshalb ich das jetzt für Java adaptieren möchte, die Unterschiede zu meiner alten plattform machen mich aber inzwischen etwas unruhig 😉


Das mit der Ableitung des TableModels hab ich mir schon beinahe gedacht, da muss ich mich wohl erst durchsehen, danke jedenfalls für die Antwort 🙂
 
So, mein neues Problem:

Ich hab mir jetzt ein eigenes TableModel abgeleitet, aber sobald ich das Applet initialisieren will, passiert nichts mehr. Das soll eine Tabelle mit 3 Spalten sein, in der 1. und 3. jeweils ein String und in der zweiten Spalte ein Objekt einer von mir erstellten Klasse:
Java:
class ElementTable extends AbstractTableModel
	{
	private String[] lines;
	private MeinElement[] elements;
	private String[] comments;

	public String[] headers = {"", "Element", "Kommentar" };
	public Class[] columnClasses = {String.class, MeinElement.class, String.class };

	public int getColumnCount() { return 3; }
	public int getRowCount()    { return elements.length;}

	public MeinElement getValueAt(int row, int col)
		{
		return elements[row];
		}

	public void setValueAt(MeinElement aValue, int row) 
		{
		elements[row] = aValue;
		fireTableCellUpdated(row, 2);
		}
	}



public class ElementEdit extends JApplet
	{
	protected ElementTable model = new ElementTable();
	protected JTable Table = new JTable(model);

	public void init ()
		{
		add(Table);
		}
	}
(Code zur Schilderung des Problems erheblich gekürzt, dreht mir keinen Strick, wenn da jetzt was fehlt)


Mein Problem ist jetzt, dass sobald ich das mit meinem TableModel erstelle, wird das gesamte Applet nicht mehr gestartet. Keine Fehlermeldung beim Kompilieren, im Browserfenster lese ich die Meldung "Applet gestartet" aber trotzdem leerer Bildschirm, obwohl zumindest der Rest meines GUIs angezeigt werden sollte (da ist noch viel mehr drin als im Beispielcode jetzt, hab das nur zur Übersicht nicht ins Beispiel geschrieben)

erstelle ich dagegen das Model mit
Java:
protected DefaultTableModel model = new DefaultTableModel();
ist schwupps alles da wo es sein soll, Eingabefelder, Clickbuttons, Panels, auch das Tabellenfenster...


Mach ich noch irgendwo eine Fehler, ich komm einfach nicht drauf..?
 
1. Was steckt den hinter der Klasse MeinElement?

2. musst Du public Object getValue(...) und public void setValue(Object o, int row, int col) überschreiben. Deine Methoden werden ja niemals von der Tabelle aufgerufen.
Java:
	public MeinElement getValueAt(int row, int col)
		{
		return elements[row];
		}

	public void setValueAt(MeinElement aValue, int row) 
		{
		elements[row] = aValue;
		fireTableCellUpdated(row, 2);
		}
	}
3. Bin ich mir nicht sicher, ob Du überhaupt ein eigenes TableModel brauchst. Vielleicht wäre es besser einfach einen eigenen CellRenderer für Spalte 2 zu definieren.
 
Keine Ahnung, ob ich ein eigenes TableModel brauche, das wurde nur von einem der Vorposter geraten 😉

Mein Hauptproblem ist immer noch das selbe, ich will Objekte meiner Klasse in die Tabelle speichern und wieder abfragen. Wenn ich das einem standardmäßigem TableModel abverlangen kann, ist es mir nur recht.

Was deine Fragen angeht:
1.)
Ist in einem früheren Post von mir weiter oben beschrieben.
Diese Klasse beschreibt geometrische Objekte, genauergesagt Punkte, Geraden, Kreisbögen, Klothoiden, kubische Parabeln und Kurven siebter Ordnung.
Also eine Variable definiert den Typ des Objektes und je nach Typ definieren mehrere Variablen die Parameter des Elements, wie Bogenlänge, Öffnungswiunkel, Radien, etc.
Des Weiteren sind in der Klasse mehrere Funktionen enthalten. Beispielsweise bei gegebenem Startpunkt und Starttangente den Endpunkt und Endtangente ausgeben, abhängig von den Parametern des Elements.


2.)
Ja, getValueAt und SetValueAt muss ich überschreiben.
Vielleicht braucht die Tabelle diese Funktionen nicht, aber ich brauche sie in späterer Folge, da ich von Objekt zu Objekt jede Menge Berechnungen ablaufen lassen muss. Also am Anfang der Liste anfangen, die Werte des ersten Objektes auslesen, damit Berechnungen durchführen und die Ergebnisse an das zweite Objekt übertragen und mit diesem weiter rechnen, usw usw... bis das Ende der Liste erreicht ist.


Abgesehen davon, muss man diese ersten drei Funktionen überschreiben, wenn man von AbstractTableModel ableitet. Steht in der Dokumentation und der Compiler sagt einem das selbe, wenn man es nicht tut ;-)



PS:
die Klasse heisst bei mir natürlich nicht "MeinElement", nur die tatsächlichen Namen und Formulierungen kann ich aus rechtlichen Gründen hier nicht angeben. Nur falls du dich wunderst...
 
Zuletzt bearbeitet:
Sorry 2. sollte eigentlich keine Frage sein, meinte eigentlich:
Du musst public Object getValue(...) und nicht public MeinElement getValue(...) überschreiben mit letzterem kann das TableModel nichts anfangen.

Vielleicht kannst Du ja mal Deinen Code auf's wesentliche reduziert aber trotzdem kompilierbar posten (KSKB) der zum einen Deine Vorgehensweise und zum anderen Dein Problem zeigt.
 
Das wirft mir jetzt aber eine Frage auf:

wenn ich
Java:
public Object getValueAt(int row, int col)
überschreibe, dann liefert diese Funktion doch den Typ "Object" zurück und kein "MeinElement". Dann könnt ich ja gleich wieder das normale TableModel verwenden, wenn der geladene Wert aus der Tabelle ein Object ist, was mir wieder die Frage aufruft, ob
Java:
Element=(MeinElement) Object
also ein stupider Cast von Object auf meine Klasse, funktioniert.



Hier jetzt eine (kompilierbare) Miniaturversion meines Programms:
Java:
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

class Element
	{
	protected int typ;
	public Element() { this.typ=0; }
	}


class ElementTable extends AbstractTableModel
	{
	private Element[] elements;
	public String[] headers = {"Element"};
	public Class[] columnClasses = { Element.class };

	public int getColumnCount() { return 1; }
	public int getRowCount()    { return elements.length;}

	public Element getValueAt(int row, int col)
		{
		return elements[row];
		}

	public void setValueAt(Element aValue, int row) 
		{
		elements[row] = aValue;
		fireTableCellUpdated(row, 1);
		}
	}

public class ElementEdit extends JApplet
	{
	protected Box		VBox = Box.createVerticalBox();
	protected JButton	Button;

	protected JTable	Table;
	protected ElementTable	model = new ElementTable();

	protected Element	pufferelement = new Element();


	public void init ()
		{
		Button = new JButton("in Tabelle ablegen");
		Button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ablegen(); } });
		VBox.add(Button);

		Button = new JButton("aus Tabelle laden");
		Button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { laden(); } });
		VBox.add(Button);

		Table = new JTable(model);
		VBox.add(Table);

		add(VBox);
		}

	protected void ablegen()
		{
		model.setValueAt(pufferelement,Table.getSelectedRow(),1);
		showStatus("Element abgelegt, Typ: "+pufferelement.typ);
		}

	protected void laden()
		{
		pufferelement=model.getValueAt(Table.getSelectedRow(),1);
		showStatus("Element geladen, Typ: "+pufferelement.typ);
		}
	}
Die Funktionen, die Zeilen der Tabelle hinzufügen, habe ich vorerst mal weggelassen, da ja selbst das schon nicht mehr ausführbar ist. Laut Statusleiste wird das Applet gestartet und dann ist Sense. Nichtmal die beiden Clickbuttons sind sichtbar.

Deklariere ich das TableModel dagegen als DefaultTableModel, lädt alles so wie es sein sollte, lediglich die Funktionsaufrufe getValueAt und setValueAt in den Funktionen ganz am Ende muss man dann entfernen, da die Übergabewerte dann ja nicht mehr stimmen.


Danke jedenfalls schon vorab für eure Geduld mit mir 🙂
 
Zuletzt bearbeitet:
Java:
public Object getValueAt(int row, int col)
überschreibe, dann liefert diese Funktion doch den Typ "Object" zurück und kein "MeinElement". Dann könnt ich ja gleich wieder das normale TableModel verwenden
Genau, das versuche ich ja zu erklären. Du musst in Deinem TableModel public Object getValueAt(int row, int col) überschreiben, denn das ist die Methode die von der Tabelle aufgerufen wird, um festzustellen was in der Zelle gezeichnet werden soll.
Du kannst auch eine public Element getValueAt(int row, int col) schreiben, die wird von der Tabelle aber ignoriert. Und dein Code lässt sich nicht komplieren, weil Dein TableModel eben diese public Object getValueAt(int row, int col) nicht besitzt.
Ich weiss zwar nicht wie Du Dein Element in der Tabelle darstellen willst, aber ich habe hier mal ein KSKB mit einen TableCellRender - denke das ist eher was, was Dir weiterhilft:
Java:
import javax.swing.*;
import javax.swing.table.*;

import java.awt.*;
import java.awt.*;
 
public class ElementEdit extends JApplet {
    private JTable    table;
 
    public void init () {
        table = new JTable();
        table.setModel(new DefaultTableModel(new Object[][] {
        		{"normaler Text", new Element(0), "Kommentar zum Element"},
        		{"normaler Text", new Element(1), "Kommentar zum Element"},
        		{"normaler Text", new Element(2), "Kommentar zum Element"},
        		{"normaler Text", new Element(3), "Kommentar zum Element"},
        		{"normaler Text", "nur normaler Text", "Kommentar zum Element"}
        }, new Object[] {"Spalte 1", "Element", "Kommentar"}));
        table.getColumnModel().getColumn(1).setCellRenderer(new ElementRenderer());
        add(table);
    }
}

class Element {
	private int typ =-1;
    
    public Element(int typ) {
    	this.typ=typ;
    }
    
    public int getTyp() {return this.typ;}
}

class ElementRenderer implements TableCellRenderer {
	private JLabel label = new JLabel();
	private Element element;
	
	public ElementRenderer() {
		label = new JLabel();
		label.setOpaque(true);
	}
	
	public Component getTableCellRendererComponent(JTable table,
		Object value, boolean isSelected, boolean hasFocus,
		int row, int column) {
		
		if (value instanceof Element) {
			element = (Element)value;
			label.setText("Element vom Typ " + element.getTyp());
			switch (element.getTyp()) {
			case 0: label.setBackground(Color.WHITE);break;
			case 1: ;
			case 2: label.setBackground(Color.GREEN);break;
			case 3: label.setBackground(Color.BLUE);break;
			default: label.setBackground(Color.RED);break;
			}
		}
		else {
			this.label.setText("FEHLER!!!");
			label.setBackground(Color.RED);
		}
		return label;
	}
}
 
Hat eine Weile gedauert, bis ich mich durch das durchgeblickt habe, aber jetzt läuft alles wie geschmiert. Ich danke dir für deine umfassende Antwort!

Thema kann als gelöst markiert werden (kann ich das selber machen?)
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben