# JTable: Zellenhöhe mit neuem CellRenderer & Editor



## GRudiD (28. Dez 2005)

Hallo,

ich habe eine JTable geschrieben, der die Daten in einem Vector mit den Objekten vom Typ MyEntry speichert. 
MyEntry beinhaltet Werte wie z.B. ID, Titel, Hersteller, etc. 

Ich habe dazu einen CellRenderer geschrieben, der aus dem MyEntry-Objekt Titel und ID ausliest und in einem 2-zeiligen JLabel zurückgibt. Dabei habe ich die Zeilenhöhe mit

```
int labelHeight = (int) this.getMinimumSize().getHeight();
int tableRowHeight = table.getRowHeight(row);

if (labelHeight != tableRowHeight) {
	table.setRowHeight(row, labelHeight);
}
```
angepasst.

Der CellEditor ist ein JPanel, wlches mehrere JTextField zum Bearbeiten enthält. Auch da wollte ich die Zellenhöhe mit dem obigen Code an die Größe des JPanel anpassen.

Die Probleme dabei:
Wenn ich eine Zelle editieren will, scheint Java erst den CellEditor auszuführen (Zellen wird größer) und anschließend gleich den CellRenderer drüberzulegen. Das kann ich beheben, in dem ich entweder im Editor oder Renderer die Zellen in der Höhe nicht anpasse. Dabei ist die Zelle nach dem editieren allerdings zu groß bzw. zu klein.

Wie kann ich das Problem lösen?


Code des CellRenderers

```
package de.cybercosmos.JStocktaking;

import java.awt.Component;
import java.io.File;

import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.View;

import de.cybercosmos.Utils.EntryConstants;
import de.cybercosmos.Utils.EntryItem;

public class EntryJTableCellRenderer extends JLabel implements TableCellRenderer {

	private Border unselectedBorder = null;
	private Border selectedBorder = null;
	private boolean isBordered = true;

	public EntryJTableCellRenderer() {
		this.setOpaque(true);
		this.setVerticalTextPosition(TOP);
		this.setHorizontalTextPosition(RIGHT);
	}

	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
		if (isSelected) {
			setForeground(table.getSelectionForeground());
			setBackground(table.getSelectionBackground());
		} else {
			setForeground(table.getForeground());
			setBackground(table.getBackground());
		}

		this.setFont(table.getFont());
		if (hasFocus) {
			setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
			if (table.isCellEditable(row, column)) {
				setForeground(table.getSelectionForeground());
				setBackground(table.getSelectionBackground());
			}
		} else {
			setBorder(new EmptyBorder(1, 1, 1, 2));
		}

		this.setText("<html><body><span style=\"color:gray; width:100px\">"
				+ ((EntryItem) value).getID()) + "</span>
" + ((EntryItem) value.getTitle())
				+ "</body></html>");

		int labelHeight = (int) this.getMinimumSize().getHeight();
		int tableRowHeight = table.getRowHeight(row);

		if (labelHeight != tableRowHeight) {
			table.setRowHeight(row, labelHeight);
		}

		return this;
	}
}
```

Code des CellEditors

```
public class EntryJTableCellEditor extends AbstractCellEditor implements TableCellEditor {

	private EntryItem currentEntry;
	private JPanel contentPanel;

	public EntryJTableCellEditor() {
		this.contentPanel = new JPanel();
		this.contentPanel.add(new JTextField());
// etc.
	}

	public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
		this.currentEntry = (EntryItem) value;

		int preferredHeight = (int) this.contentPanel.getMinimumSize().getHeight();
		int tableRowHeight = table.getRowHeight(row);

		if (preferredHeight != tableRowHeight) {
			table.setRowHeight(row, preferredHeight);
		}

		return this.contentPanel;
	}

	public Object getCellEditorValue() {
		return this.currentEntry;
	}
}
```


----------



## André Uhres (28. Dez 2005)

Im Editor könntest du den JPanel in ein JScrollPane packen. 
Die Zeilenhöhe lässt du wie sie ist, da sie ja dann für den Editor unerheblich ist.


----------



## GRudiD (29. Dez 2005)

Ja, das ist eine Möglichkeit. 
Das Problem daran ist nur, dass das JPanel insgesamt viel größer ist als das JLabel. Es wäre also recht Benutzerunfreundlich jedesmal zum Editieren von einigen Werten (soll im JPanel geschehen) scrollen zu müssen.


----------



## André Uhres (29. Dez 2005)

Das wäre vielleicht ein Kandidat für einen Dialog (MouseListener-->Rechtsklick-->JDialog).
Dann haste unbegrenzte Möglichkeiten zum editieren.


----------



## GRudiD (30. Dez 2005)

Ich habe da noch einen anderen Ansatz:
Ich werde das Editieren der Zellen einfach auf false setzen und einen KeyListener auf der JTable anwenden. Immer wenn dann z.B. die F2 Taste gedrückt wurde, soll er mir das JPanel an die Koordianaten der selektierten Zelle zeichnen.
Das Ganze müsste dann quasi "modal" sein, damit der User nicht in der Zwischenzeit, wenn das Panel angezeigt wird das Fenster mit der JTable verschiebt. Also vielleicht doch ein Dialog (ohne Rahmen) 
Den CellEditor könnte man sich dann sparen.

Ich werde das bei Gelegenheit mal ausprobieren...


----------



## R@ID3N (10. Jan 2006)

Also, falls du es nicht schon selbst geschafft hast, oder deine andere Lösung bevorzugst, 
habe ich hier ein Verbesserung aber sicherlich noch nicht entgültige Lösung für dich.

Und zwar sollte das genauso Funktionieren wie du dir es vorgestellt hast, nur ein bissl muss dazu:


```
//das hier für den CellRenderer
if (labelHeight > rowHeight && hasFocus == true && isSelected == true)
{
	table.setRowHeight(row, labelHeight);
}

//das hier für den CellEditor
if (labelHeight > rowHeight && isSelected == true)
{
	table.setRowHeight(row, labelHeight);
}
```

Da ich mit diesen Methoden an einer Probetabelle gearbeitet habe, weiß ich nicht ob sie bei dir 100% Funktionieren,
aber durch die &&-Verknüpfungen habe ich es bei mir geschafft die Vollauslastung meines Prozessors wieder auf
Normalbetrieb zu bekommen.

Falls du es schaffst deinen Editor so zu erweitern, dass automatisch bei Texteingabe deine Zelle mitangepasst wird,
lass es mich bitte wissen, ich könnte das nämlich auch sehr gut gebrauchen 

Gruß R@ID3N


----------



## R@ID3N (11. Jan 2006)

Hallo, ich bins schon wieder...

Auch wenn ich nicht viel Hoffnung habe, dass der Schöpfer dieses Threads irgendwann wieder mitpostet
(ich gehe da mal unverschämter Weise von den Postzahlen aus),
aber da ich selber enorme Probleme mit dieser Sache habe, und ich kaum etwas woanderst darüber
gefunden habe, werde ich den Thread noch ein bisschen am Leben erhalten.

Habe es jetzt hinbekommen, dass die Zelle beim 1. mal anwählen ihre gewollte Größe annimmt,
aber immer noch nicht während des ändern des Inhalts der Zelle.
Das geschieht dann wieder, wenn die Zelle nach dem verändern wieder editiert wird.

Quelltext sieht jetzt folgendermaßen aus:

```
//Wieder für den CellEditor
//Wichtig: Die Zeilen, die ich für den CellRenderer gepostet habe,
// wieder entfernen, dann sollte es so weit funktionieren

if (labelHeight > rowHeight)
{
   table.setRowHeight(row, labelHeight);
}
```

C Ya, R@ID3N


----------



## GRudiD (12. Jan 2006)

Keine Angst, ich werde auch noch weiter an diesem Thread schreiben. Bisher habe ich nur noch keine Hilfe bei meinen Java-Projekten gebraucht 

R@ID3N: Danke für die Antworten.
Ich habe mal versucht deine Vorschläge einzuarbeiten. Leider habe ich bei meinem CellRenderer keine hasFocus-Methode gefunden.

Bei mir sieht es momentan so aus:

```
// für den CellRenderer
if (labelHeight != rowHeight && table.getEditorComponent() == null) {
	table.setRowHeight(row, labelHeight);
}


// für den CellEditor
if (preferredHeight != rowHeight && isSelected && !table.isEditing()) {
	table.setRowHeight(row, preferredHeight);
}
```

Komischerweise ist beim CellEditor die isEditing Methode immer false wenn der Editor offen ist.

Das einzige Problem ist nun noch, dass der CellEditor "halb" aufgerufen wird, wenn ich einfach auf eine Zelle klicke. Soll heißen, wenn ich auf eine Zelle klicke wird der Editor geladen, bei einem zweiten Klick wird auch die Zellenhöhe angepasst. 
Dazu jetzt die Frage: Kann ich es irgendwie handeln, dass der CellEditor NUR bei Doppelklick bzw. F2 (o.ä.) aufgerufen  und bei einem Ereignis (Bsp. Action vom JButton) geschlossen wird?

Das Automatische Vergrößern der Zelle beim Editieren habe ich auch noch nicht hinbekommen.


----------



## GRudiD (12. Jan 2006)

OK, das mit dem Doppelklick hat man schon in einem anderen Thread gefragt ([JTable] Editor erst nach Doppelklick)

Es funktioniert jetzt so wie ich es wollte. Danke für eure Hilfe.


----------



## R@ID3N (12. Jan 2006)

Gut, dann hat zumindest einer eine vollständige Lösung ^^

Ich habe es noch geschafft, dass table.isEditing(); true zurückgibt.
Und zwar muss die abfrage im CellRenderer stattfinden.
Im CellEditor hat sie bei mir auch nur false geliefert.

Und noch zu dem hasFocus, das ist keine Methode  sondern eine boolean Variable, die bei dieser Methode:

```
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column){}
```

mit übergeben wird.

Und ich werd mich jetzt noch an die letzte Hürde, der automatischen Anpassung begeben 

C Ya


----------



## GRudiD (12. Jan 2006)

Hast ja recht  Hier die bisherigen relevanten Daten:


```
// für den CellRenderer
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

		if (preferedHeight != tableRowHeight && table.getEditorComponent() == null && !table.isEditing()) {
			table.setRowHeight(row, labelHeight);
		}
}


// für den CellEditor
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {

		if (preferedHeight != tableRowHeight && isSelected) {
			table.setRowHeight(row, preferredHeight);
		}
}

public boolean isCellEditable(EventObject event) {
	if (event instanceof MouseEvent) {
		if (((MouseEvent) event).getClickCount() == 2) {
			return true;
		}
	}
	return false;
}
```


----------



## R@ID3N (12. Jan 2006)

So, damit auch man auch mal meinen code kennt  (ist was die getTableCellEditor/RendererComponent Methoden angeht änlich aber doch etwas anderst) post ich den auch mal.
Den CellEditor hab ich mir aus dem DefaultCellEditor aus der java API gemopst und so verändert, dass er eine JTextArea verwenden kann.
Der CellRenderer sollte leicht verständlich sein.
Das wichtigste sind aber immer noch die getTableCellEditor/RendererComponent Methoden
Bin bis jetzt aber noch nicht weiter gekommen.


```
import java.awt.*;
import java.awt.event.*;
import java.io.Serializable;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
import javax.swing.AbstractCellEditor;


public class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor, KeyListener {

    protected JTextArea textArea;
    protected EditorDelegate delegate;
    protected int clickCountToStart = 1;
    protected int lineCount = 0;

    public MyTableCellEditor(final JTextArea textArea) {
        this.textArea = textArea;
        textArea.addKeyListener(this);
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        this.clickCountToStart = 1;

        delegate = new EditorDelegate()
        {
            public void setValue(Object value) {
				textArea.setText((value != null) ? value.toString() : "");
            }

	   		public Object getCellEditorValue() {
				return textArea.getText();
	    	}
        };
		textArea.addMouseListener(delegate);
    }

    public JTextArea getComponent() { //geändert, damit eine TextArea zurückgegeben wird
		return textArea;
    }

//ein Haufen Methoden entfernt

    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        delegate.setValue(value);

		int lineCount = textArea.getLineCount();
		int rowHeight = table.getRowHeight(table.getSelectedRow());
		int preferredHeight = lineCount * 16;

		if(preferredHeight > rowHeight && rowHeight != 0) {//rowHeight != 0 damit letzt row nicht am anfang auf 16 gestetzt wird
			table.setRowHeight(row, lineCount * 16);
		}

		return textArea;
    }

	protected class EditorDelegate implements MouseListener, ItemListener, Serializable {// verwende MouseListener
															//da ActionEvent von JTextArea nicht unterstützt wird

//wieder ein paar methoden weggemacht ;)


		public boolean isCellEditable(EventObject anEvent) {
			if (anEvent instanceof MouseEvent) {
				return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
			}
			return true;
		}

		//MouseEvent als ersatz für ActioneEvent
		public void mouseClicked(MouseEvent e){}
		public void mousePressed(MouseEvent e){}
		public void mouseReleased(MouseEvent e){}
		public void mouseEntered(MouseEvent e){}
		public void mouseExited(MouseEvent e){}

		public void itemStateChanged(ItemEvent e) {
			MyTableCellEditor.this.stopCellEditing();
		}
    }
}
```


```
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.table.TableCellRenderer;

public class MyTextCellRenderer extends JTextArea implements TableCellRenderer {	

	public MyTextCellRenderer() {
		this.setOpaque(true);
	}

	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) {
		JTextArea textArea = new JTextArea((String)value);
		textArea.setOpaque(true);

		int lineCount = textArea.getLineCount();
		int rowHeight = table.getRowHeight(table.getSelectedRow());
		int preferredHeight = lineCount * 16;

		if(preferredHeight > rowHeight && rowHeight != 0) {//rowHeight != 0 damit letzt row nicht am Anfang auf 16 gestetzt wird
			table.setRowHeight(row, lineCount * 16);
		}

		return textArea;
  	}
}
```


----------



## Guest (31. Jan 2006)

So, das ganze scheint ja jetzt richtig gut zu funktionieren. Draufklicken und Zellhöhe passt sich an.
Ein Problem habe ich allerdings noch:

Ich habe den JTree in einem JScrollPanel auf mein JFrame gepackt. Wenn ich jetzt nur wenig Elemente in meiner Tabelle habe (alle! Elemente sind in der JScrollPane zu sehen) oder das JFrame ist nicht maximiert dann wird beim Doppelklick die rowHeight() auch richtig angepasst aber auch gleich wieder zurückgesetzt. Das merkwürdige dabei ist, sobald ich das JFrame maximiere oder nicht alle Elemente der Tabelle in dem JScrollPane angezeigt werden können, funktioniert alles wonderbra.

Kann mir das jemand erklären?

Eine Sache noch:
Ich habe in meinem CellEditor ja ein Panel mit mehreren JTextfield Komponenten. Wie bekommt man es hin, dass man zwischen diesen auch wieder per Tab umschalten kann? Ist irgendwie verloren gegangen...


----------



## GRudiD (1. Feb 2006)

Hatte vergessen mich anzumelden. Die letzte Nachricht ist von mir


----------



## GRudiD (1. Feb 2006)

Also, Problem #1 gelöst:

Wenn durch setRowHeight die größe des JTable größer als die des JScrollPane wurde (also die vertikalen Scrollbars erst eingeblendet werden mussten), dann wurde der CellEditor nur kurz ein- und gleich wieder ausgeblendet. Das habe ich beim JScrollPane durch setzen von JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER lösen können.


----------



## R@ID3N (14. Feb 2006)

Also ich habe mein Problem auch mehr oder weniger gelöst 
Die Lösung ist zwar leicht umständlich, aber sie funktioniert.
Habe das Ganze jetzt auch mit einem kleine Popupfenster
realisiert, das geöffnet wird, wenn der cellEditor aufgerufen wird.

Dazu habe ich dann noch eine kleine Abfrage geschrieben,
die herausfindet wieviele Zeilen eingegeben wurden und
darüber dann die Zeilenhöhe berechnet.


----------



## André Uhres (15. Feb 2006)

Hatte gestern für einen anderen Thread einen älteren Schnipsel rausgekramt.
Bin nur nicht sicher ob's hier reinpasst weil ich diesen Thread nicht weiter verfolgt hab.
MultiLineCells_Demo


----------



## R@ID3N (17. Feb 2006)

Andre_Uhres hat gesagt.:
			
		

> Hatte gestern für einen anderen Thread einen älteren Schnipsel rausgekramt.
> Bin nur nicht sicher ob's hier reinpasst weil ich diesen Thread nicht weiter verfolgt hab.
> MultiLineCells_Demo


Also für mein Problem ist es ideal ^^ vielen Dank!


----------

