variable Höhe einer JTextArea (als Renderer/Editor) als Zelle einer JTable

Haymaker84

Mitglied
Hallo Leute,

ich hänge schon echt lange an einem Problem mit JTextAreas.
Ich habe eine JTable, die drei Spalten (eigentlich zwei, eine(ID-Feld) ist unsichbar) hat, von denen eine manuell zu bearbeiten ist.
Die linke enthält eine Textnotiz (bis 150 Zeichen) und die rechte enthält automatisch den Namen des Bearbeiters und einen Timestamp.

Von der Funktion her ist es so, dass am Beginn (Aufruf der Oberfläche) die schon bestehenden Notizen aus der Datenbank geladen werden
und neue hinzugefügt werden können. Das funktioniert alles ganz toll, mit der Ausnahme der variablen Zeilenhöhe.

So wie ich es bis jetzt gesehen habe, ist es fast nicht möglich, herauszufinden wie viele Zeilen in einer JTextare nun gerade dargestellt werden.

Dies ist mein Ansatz (aus einem Anderen Forum):

Java:
    private static ArrayList<String> getWrappedLines(JTextArea myTextArea) {

        ArrayList<String> linesList = new ArrayList<String>();
        int length = myTextArea.getDocument().getLength();
        int offset = 0;
        
        try {
            while (offset < length) {
                int end = Utilities.getRowEnd(myTextArea, offset);

                if (end < 0) {
                    break;
                }

                // Include the last character on the line
                end = Math.min(end + 1, length);
                String line = 
                    myTextArea.getDocument().getText(offset, end - offset);

                // Remove the line break character
                if (line.endsWith("\n")) {
                    line = line.substring(0, line.length() - 1);
                }

                linesList.add(line);
                offset = end;
            }
        } catch (BadLocationException e) {
            System.err.println("Bad Location at TextArea");
        }
        return linesList;
    }

Dieser Code nimmt eine TextArea und liest aus, wie viele Zeilen automatisch gewrappt werden.
Bei manuellen Umbrüchen (\n) führt dies allerdings zu Exceptions (Bad Location), was mir
recht egal ist, da Ich Enter-Keys abfange und damit nur die Eingabe bestätige.

Nun liefert diese Methode oben die Zeilen Zurück, deren Anzahl ich mit der Höhe einer einzelnen Zeile multipliziere
und als neue Größe an die Row als Höhe übergebe.

Java:
    public static void setOptimalRowHeight(JTable table, JTextArea textArea, int row, int column) {
    
        if (
            row >= 0 &&
            column == VerwaltungsnotizController.IND_VNO_NOTIZ    
        ) {
            int fontHeight = 
                textArea.getFontMetrics(textArea.getFont()).getHeight();
            
            ArrayList<String> wrappedLines = getWrappedLines(textArea);
            int nrOfLines = wrappedLines.size();

            int oldSize = table.getRowHeight(row);
            int newSize = (fontHeight * nrOfLines) + 5;

            if (newSize != oldSize) {
                table.setRowHeight(row, newSize);
            }
        }
    }
	
	public static void setOptimalRowHeight(JTable table, JTextArea textArea) {

	int row = table.getSelectedRow();
	int column = table.getSelectedColumn();

	if (
		row >= 0 &&
		column == VerwaltungsnotizController.IND_VNO_NOTIZ    
	) {
		setOptimalRowHeight(table, textArea, row, column);
	}

Dieser Mechanismus wird von zwei Komponenten aufgerufen, dem CellEditor (bei dem es ansich ganz gut geht) und dem
CellRender (bei dem es miserabel funktioniert)

CellEditor (mit dazugehörendem Listener):
Java:
public class VnoCellEditor extends AbstractCellEditor implements TableCellEditor {

    private VnoTextArea textArea;
    private JTable table;

    public VnoCellEditor (JTable table) {
       
        this.table = table;
        textArea = new VnoTextArea(table);
        
        textArea.setMaximumInputLength(VerwaltungsnotizController.MAX_INPUT_VNO);
        
        textArea.addKeyListener(new TextAreaEnterAdapter());
        
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        
        Document document = textArea.getDocument();
        document.addDocumentListener(new VnoTextAreaDocListener(table, textArea));
    }

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

        if (value instanceof String) {
            textArea.setText((String)value);
        } else if (value instanceof Integer) {
            Integer intValue = (Integer)value;
            textArea.setText(intValue.toString());
        } else {
            Object objValue = value;
            textArea.setText(objValue.toString());
        }
        
        System.err.println("Editor - get Component");
        
        return textArea;
    }

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

public class VnoTextAreaDocListener implements DocumentListener {

    private JTable table;
    private JTextArea textArea;

    public VnoTextAreaDocListener(JTable table, JTextArea textArea) {
        this.table = table;
        this.textArea = textArea;
    }

    public void insertUpdate(DocumentEvent e) {
        VnoTableRowHeightHelper.setOptimalRowHeight(table, textArea);
    }

    public void removeUpdate(DocumentEvent e) {
        VnoTableRowHeightHelper.setOptimalRowHeight(table, textArea);
    }

    public void changedUpdate(DocumentEvent e) {
        //Plain text components don't fire these events
    }
}

Bei dem Editor funktioniert es für mich so, wie ich es mir vorgestellt habe!
Man befindet sich in der Zelle im Editiermodus und betätigt eine Taste, z.B. "A" wonach im Listener die insertUpdate
Methode ausgeführt wird und die Höhe perfekt bestimmt wird.

!Allerdings! hab ich an der Stelle das Problem, dass das Wechseln der Zeile bei mir auch ein removeUpdate-Event
auslöst und dadurch, dass der Focus schon woanders liegt die zeilenhöhe einer anderen Zeile bestimmt.
Keine Ahnung, wodurch das Event ausgelöst wird...

CellRenderer:
Java:
public class VnoCellRenderer implements TableCellRenderer {

    private VnoTextArea textArea;

    private int Row = -1;
    private int Column = -1;
    
    private VnoTableModel jtmVno;

    public VnoCellRenderer(JTable table) {
    
        this.jtmVno = (VnoTableModel)table.getModel();
        
        textArea = new VnoTextArea(table);
        textArea.setLayout(new BorderLayout());
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
    }
    
    public VnoTextArea getTextArea() {
        
        return textArea;
    }

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

        Row = row;
        Column = column;

        String tmpValue = null;

        if (value instanceof String) {
            tmpValue = (String)value;
        } else if (value instanceof Integer) {
            tmpValue = ((Integer)value).toString();
        } else {
            tmpValue = value.toString();
        }

        // FARBEN:
        // Normalmodus
        if (jtmVno == null) {
            System.err.println("Table Model noch nicht initialisiert!");
            textArea.setBackground(Color.LIGHT_GRAY);
        } else {
            if (jtmVno.isCellEditable(Row, Column)) {
                textArea.setBackground(Color.WHITE);
            } else {
                textArea.setBackground(Color.LIGHT_GRAY);
            }
        }
        textArea.setForeground(Color.BLACK);

        // Für ausgewählte Zeile
        if (isSelected && !hasFocus) {
            textArea.setBackground(CommonConstants.SELECTION_COLOR_BLUE);
            textArea.setForeground(Color.WHITE);
        }

        textArea.setText(tmpValue);
        textArea.setEditable(true);
		
        if (Column == VerwaltungsnotizController.IND_VNO_NOTIZ) {
            VnoTableRowHeightHelper.setOptimalRowHeight(table, textArea, row, column);
        }		

        return textArea;
    }
}

Hier wird das Anpassen der Zeilenhöhe logischerweise nicht durch Listener gewährleistet,
sondern am Ende der Methode getTableCellRendererComponent(), also immer dann, wenn gerendert wird!
Das Problem hierbei liegt darin, dass an der Stelle nicht ermittelt werden kann, wie viele Zeilen gewrappt vorliegen.
Die methode getWrappedLines() gibt immer eine leere ArrayList zurück, da Utilities.getRowEnd(myTextArea, offset);
an der Stelle immer "-1" zurück gibt.
Ich vermute es liegt daran, dass ich den Mechanismus zu Früh aufrufe, da an der Stelle dass Rendern eventuell nicht abgeschlossen ist
oder gar garnicht angefangen wurde und man für diese Funktion warscheinlich eine fertig gerenderte Area benötigt?!
Aber wenn man es an der Stelle nicht aufruft, wann denn dann?!

Btw hab ich mir eine eigene TextArea erstellt, wg. max anzahl Buchstaben und weil Area und Table eng verzahnt sind...:
Java:
public class VnoTextArea extends JTextArea {

    private JTable table;

    private int _maximumInputLength = 0;

    public VnoTextArea(JTable table) {
    
        this.table = table;
    }
    
    public void setMaximumInputLength(int maximumInputLength) {
      if (maximumInputLength != 0) {
      
        _maximumInputLength = maximumInputLength;

        super.setDocument(new PlainDocument() {
            public void insertString(int offset, String string, AttributeSet attributeSet) throws BadLocationException {
              StringBuffer buffer = new StringBuffer(string);
              int size = VnoTextArea.this.getText().length();

              int bufferSize = buffer.length();

              if ((size + bufferSize) > _maximumInputLength) {
                buffer.delete((_maximumInputLength - size), bufferSize);
              }

              if (size < _maximumInputLength) {
                super.insertString(offset, buffer.toString(), attributeSet);
              } else {
                Toolkit.getDefaultToolkit().beep();
              }
            }
          });
      }
    }
}

Tut mir übrigens sehr leid, dass ich hier kein direkt ausführbares Beispiel geben kann,
Es handelt sich um ein relativ grosses Projekt. Alle Abhängigkeiten hätten den Rahmen gesprengt.
Und das meiste darf ich garnicht veröffentlichen...

Aber ich denke mein Problem lässt sich hier ganz gut isoliert betrachten.

Ich hatte auch schon andere Ansätze probiert, z.B.
- die preferredSize der TextArea abfragen (hat irgendwie garnichts gemacht)
- den LineCount der TextArea abfragen (zählt nur "\n")
- Annehmen, dass durchschnittlich ca. 20 Zeichen in eine Zeile passen und den Inhalt dann dividieren
um an einen theoretischen Linecount zu kommen (dazu muss ich wohl nicht viel sagen...)

Nun ja - dass was ich hier dargestellt habe, ist für mich bis jetzt der vielsprechendste Ansatz, allerdings bin
soweit mit meinem Swing-Latein am Ende und stochere seit Tagen in der Sackgasse im dunkeln.

Was würdet ihr jetzt machen?!


vielen Dank im Voraus schon mal für eventuelle Mühen,
Haye
 
Ist es denn so, dass ich mein Problem verständlich dargestellt habe.
Bzw. sind weitere Code-Auszüge vonnöten?!

Oder ist gar mein ganzer Ansatz nicht nachzuvollziehen?

Oder vielleicht mal von der anderen Richtung gefragt:
Wenn ihr eine Tabelle mit mehrzeiligem Inhalt anlegen wolltet -
Welchen Ansatz würdet ihr verfolgen?
(Damit der Text lesbar bleibt?)
 
Zuletzt bearbeitet:
Vorausgesetzt der Text enthält Zeilenumbrüche, dann kann man ihn relativ einfach per TextArea als Renderer in der Tabelle mehrzeilig anzeigen. Die benötigte Höhe bekommt man über die PreferredSize der Area. Das setzen der Zeilenhöhe darf nicht im Renderer selbst erfolgen (==> führt bei der Standardimplementierung im JTable zu einer Endlosschleife).

Beispiel:
Java:
import java.awt.BorderLayout;
import java.awt.Component;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

public class TextAreaRendererDemo extends JFrame {
	private JTable table;

	public TextAreaRendererDemo() {
		table = new JTable(
				new Object[][] {
						{ "test", "aaaaaaaaaabbbbbbbbbbcccccccccceeeeeeeeeeffffffffff" },
						{ "test", "aaaaaaaaaa\nbbbbbbbbbbcccccccccc\neeeeeeeeee\nffffffffff" },
						{ "test", "aaaaaaaaaa\nbbbbbbbbbb\ncccccccccc\neeeeeeeeee\nffffffffff" } },
				new String[] { "A", "B" });
		this.getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
		table.getColumnModel().getColumn(1).setCellRenderer(
				new TextAreaRenderer());
		table.getModel().addTableModelListener(new TableModelListener() {
			public void tableChanged(TableModelEvent evt) {
				for (int row = evt.getFirstRow(); row <= evt.getLastRow(); row++) {
					calculateAndSetRowHeigth();
				}
			}
		});
		this.calculateAndSetRowHeigth();
	}

	private void calculateAndSetRowHeigth() {
		for (int row = 0; row < table.getRowCount(); row++) {
			TableCellRenderer renderer;
			renderer = table.getCellRenderer(row, 1);
			int h = table.prepareRenderer(renderer, row, 1).getPreferredSize().height;

			table.setRowHeight(row, h);
		}
	}

	class TextAreaRenderer extends DefaultTableCellRenderer {
		private JTextArea area;

		public TextAreaRenderer() {
			area = new JTextArea();
			area.setLineWrap(true);
			area.setWrapStyleWord(true);
		}

		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			Component c = super.getTableCellRendererComponent(table, value,
					isSelected, hasFocus, row, column);
			area.setText(value.toString());
			area.setForeground(c.getForeground());
			area.setBackground(c.getBackground());
			return area;
		}
	}

	public static void main(String[] args) {
		JFrame frame = new TextAreaRendererDemo();
		frame.setBounds(0, 0, 500, 300);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
}
 
Danke erstmal für deine Antwort!

Vorausgesetzt der Text enthält Zeilenumbrüche [...]

Hier liegt der Hund doch schon begraben, das funktioniert zwar schön für die manuellen line breaks
"\n", die automatischen Wordwraps werden dabei wiederum doch nicht berücksichtigt.
Und in meinem Ansatz war es so, dass manuelle line breaks gar technisch nicht erlaubt waren.
Hab noch keinen Ansatz (auch diesen nicht) gesehen, der beide Arten von Zeilenumbrüche zu einem
korrekten line-count verarbeiten kann!

Das setzen der Zeilenhöhe darf nicht im Renderer selbst erfolgen (==> führt bei der Standardimplementierung im JTable zu einer Endlosschleife).

Ja und nein. Man kann sehr wohl aus dem Renderer die Höhe bestimmen - Vorausgesetzt:
Man wählt eine Spalte aus, die dabei den ton angibt!

Java:
if (Column == VerwaltungsnotizController.IND_VNO_NOTIZ) {}

Würde auch mit mehreren Spalten gehen, wenn diese ein definiertes Verhältnis zueinander haben...
 
Ja und nein. Man kann sehr wohl aus dem Renderer die Höhe bestimmen
Bestimmen schon, aber das Setzen der Zeilenhöhe (setRowHeight(...)) darf nicht im Renderer bzw. in dessen getTableCell... passieren, da ein Setzen der Zeilenhöhe ein Neuzeichnen der Zellen bewirkt.

Hier mal ein anderer Ansatz, der auch bei automatischem Zeilenumbruch funktionert:
Java:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;

public class TextAreaRendererDemo extends JFrame {
	private JTable table;

	public TextAreaRendererDemo() {
		table = new JTable(
				new Object[][] {
						{ "test", "aaaaaaaaaabbbbbbbbbb cccccccccceeeeeeeeeeffffffffff aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
						{ "test", "aaaaaaaaaa bbbbbbbbbbcccccccccc eeeeeeeeee ffffffffff" },
						{ "test", "aaaaaaaaaa bbbbbbbbbb cccccccccc eeeeeeeeee ffffffffff" } },
				new String[] { "A", "B" });
		
		this.getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);

		table.getColumnModel().getColumn(1).setCellRenderer(new TextAreaRenderer());
		
		table.getModel().addTableModelListener(new TableModelListener() {
			public void tableChanged(TableModelEvent evt) {
				for (int row = evt.getFirstRow(); row <= evt.getLastRow(); row++) {
					calculateAndSetRowHeigth();
				}
			}
		});
		
		table.addComponentListener(new ComponentListener() {
			public void componentHidden(ComponentEvent e) {}
			public void componentMoved(ComponentEvent e) {}

			public void componentResized(ComponentEvent e) {
				calculateAndSetRowHeigth();
			}

			public void componentShown(ComponentEvent e) {
				calculateAndSetRowHeigth();
			}
		});
	}

	private void calculateAndSetRowHeigth() {
		int width = table.getColumnModel().getColumn(1).getWidth();
		for (int row = 0; row < table.getRowCount(); row++) {
			JTextArea area = new JTextArea();
			area.setLineWrap(true);
			area.setWrapStyleWord(true);
			area.setSize(width, Short.MAX_VALUE);
			area.setText(table.getValueAt(row, 1).toString());
			System.out.println(area.getPreferredSize().height);
			table.setRowHeight(row, area.getPreferredSize().height);
		}
	}

	class TextAreaRenderer extends DefaultTableCellRenderer {
		private JTextArea area;

		public TextAreaRenderer() {
			area = new JTextArea();
			area.setLineWrap(true);
			area.setWrapStyleWord(true);
		}

		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			Component c = super.getTableCellRendererComponent(table, value,
					isSelected, hasFocus, row, column);
			area.setText(value.toString());
			area.setForeground(c.getForeground());
			area.setBackground(c.getBackground());
			return area;
		}
	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable(){
			public void run() {
				final JFrame frame = new TextAreaRendererDemo();
				frame.setBounds(0, 0, 500, 300);
				frame.setLocationRelativeTo(null);
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setVisible(true);
			}
		});
	}
}
 
oh ja, jetzt sehe ich es auch. Ich konnte davon schon einiges in meine Anwendung übertragen.
Besonders die Sachen mit dem TableModelListener und dem ComponentListener haben mir sehr gut gefallen!

Allerding ist es, so wie du es jetzt aufgezogen hattest, nur eine halbe Lösung, da das wiederum
nur für den Renderer zu funktionieren scheint.
Ich ahb mal die Version durch einen Editor mit DocumentListener erweitert, so wie ich ihn
im Moment in meiner Anwendung einsetze.

Java:
package jtextareainjtable;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

import javax.swing.AbstractCellEditor;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.text.Document;

public class TextAreaRendererDemo extends JFrame {
    private JTable table;

    public TextAreaRendererDemo() {
        table = 
                new JTable(new Object[][] { { "test", "aaaaaaaaaabbbbbbbbbb cccccccccceeeeeeeeeeffffffffff aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, 
                                            { "test", 
                                              "aaaaaaaaaa bbbbbbbbbbcccccccccc eeeeeeeeee ffffffffff" }, 
                                            { "test", 
                                              "aaaaaaaaaa bbbbbbbbbb cccccccccc eeeeeeeeee ffffffffff" } }, 
                           new String[] { "0", "1" });

        this.getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);

        table.getColumnModel().getColumn(1).setCellRenderer(new TextAreaRenderer());
        table.getColumnModel().getColumn(1).setCellEditor(new TextAreaEditor());

        table.getModel().addTableModelListener(new TableModelListener() {
                    public void tableChanged(TableModelEvent evt) {
                        for (int row = evt.getFirstRow(); 
                             row <= evt.getLastRow(); row++) {
                            calculateAndSetRowHeigth();
                        }
                    }
                });

        table.addComponentListener(new ComponentListener() {
                    public void componentHidden(ComponentEvent e) {
                    }

                    public void componentMoved(ComponentEvent e) {
                    }

                    public void componentResized(ComponentEvent e) {
                        calculateAndSetRowHeigth();
                    }

                    public void componentShown(ComponentEvent e) {
                        calculateAndSetRowHeigth();
                    }
                });
    }

    private void calculateAndSetRowHeigth() {
        int width = table.getColumnModel().getColumn(1).getWidth();
        for (int row = 0; row < table.getRowCount(); row++) {
            JTextArea area = new JTextArea();
            area.setLineWrap(true);
            area.setWrapStyleWord(true);
            area.setSize(width, Short.MAX_VALUE);
            area.setText(table.getValueAt(row, 1).toString());
            System.out.println(area.getPreferredSize().height);
            table.setRowHeight(row, area.getPreferredSize().height);
        }
    }
    
    class TextAreaEditor extends AbstractCellEditor implements TableCellEditor {

        private JTextArea area;

        public TextAreaEditor() {
            area = new JTextArea();
            area.setLineWrap(true);
            area.setWrapStyleWord(true);
            
            Document document = area.getDocument();
            document.addDocumentListener(new TextAreaEditorDocListener());
        }

        public Component getTableCellEditorComponent(JTable table, 
                                                     Object value, 
                                                     boolean isSelected, 
                                                     int row, int column) {
            
            if (value instanceof String) {
                area.setText((String)value);
            } else if (value instanceof Integer) {
                Integer intValue = (Integer)value;
                area.setText(intValue.toString());
            } else {
                Object objValue = value;
                area.setText(objValue.toString());
            }
            
            return area;
        }

        public Object getCellEditorValue() {
            return area.getText();
        }
    }
    
    class TextAreaEditorDocListener implements DocumentListener {

        public void insertUpdate(DocumentEvent e) {

            calculateAndSetRowHeigth();
        }

        public void removeUpdate(DocumentEvent e) {

            calculateAndSetRowHeigth();
        }

        public void changedUpdate(DocumentEvent e) {
            //Plain text components don't fire these events
        }
    }
    

    class TextAreaRenderer extends DefaultTableCellRenderer {
        private JTextArea area;

        public TextAreaRenderer() {
            area = new JTextArea();
            area.setLineWrap(true);
            area.setWrapStyleWord(true);
        }

        public Component getTableCellRendererComponent(JTable table, 
                                                       Object value, 
                                                       boolean isSelected, 
                                                       boolean hasFocus, 
                                                       int row, int column) {
            Component c = 
                super.getTableCellRendererComponent(table, value, isSelected, 
                                                    hasFocus, row, column);
            area.setText(value.toString());
            area.setForeground(c.getForeground());
            area.setBackground(c.getBackground());
            return area;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        final JFrame frame = new TextAreaRendererDemo();
                        frame.setBounds(0, 0, 500, 300);
                        frame.setLocationRelativeTo(null);
                        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                        frame.setVisible(true);
                    }
                });
    }
}

Tut mir leid, ich möchte jetzt nicht so wirken als müsste man mir ALLES vorkauen. Aber ich habe selbst auch mehrere verschiedene Ansätze probiert, die entweder nur für den Renderer oder nur für den Editor gut funktioniert haben. An der stelle befand ich mich immer in der Sackgasse.

Hier ist es ähnlich. Die funktion sieht super aus, allerdings wüsste ich nicht, was ich auf anhieb machen muss, damit es für den Editor passt?!
Ich kann ja auch nicht recht diesen hier für den Renderer einsetzen und parallel für den Editor eine andere Art der Berechnung einsetzen - was natürlich sehr unschön wäre und immer unterschiede enthalten würde, die den Anwender verwirren.


Haye
 
Würde es nicht ausreichen als Editor eine JScrollPane (die ein JTextArea beinhaltet) zu verwenden?

Ansonsten könnte man auch einen JDialog mit JScrollPane und JTextArea zum Editieren der Tabellenzellen anzeigen lassen.
 
[...] als Editor eine JScrollPane (die ein JTextArea beinhaltet) [...]

Könnte klappen, dann würde man wohl eine feste Höhe definieren. Man würde durch die anzeigen stärker merken,
wenn man im editiermodus ist und wann nicht.
Allerdings würde dann auch stärker dieser Effekt auftreten:

http://www.java-forum.org/awt-swing-swt/108277-jtable-edit-render-modus-setzen.html


[...] JDialog mit JScrollPane und JTextArea zum Editieren [...]

Über die Variante hab ich auch schon recht früh nachgedacht. Allerdings wollte ich sieh mir als allerletzte möglichkeit
aufsparen, wenn ich eingesehen habe, dass ich die urpsrüngliche Variante mit meiner Erfahrung nicht hinbekomme.
So langsam wär es wohl an der Zeit, diese Möglichkeit in Betracht zu ziehen.
 
Mildert folgendes diesen Effekt?
Java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;

public class TextAreaRendererDemo extends JFrame {
	private JTable table;

	public TextAreaRendererDemo() {
		table = new JTable(new Object[][] {
						{"test", "aaaaaaaaaabbbbbbbbbb cccccccccceeeeeeeeeeffffffffff aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
						{"test", "aaaaaaaaaa bbbbbbbbbbcccccccccc eeeeeeeeee ffffffffff" },
						{ "test", "aaaaaaaaaa bbbbbbbbbb cccccccccc eeeeeeeeee ffffffffff" } },
				new String[] { "0", "1" }) {
			public boolean editCellAt(int row, int column) {
				boolean b = super.editCellAt(row, column);
				if (column==1)
					((TextAreaEditor)table.getCellEditor(row, 1)).specialActivate();
				return b;
			}
		};

		this.getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);

		table.getColumnModel().getColumn(1).setCellRenderer(
				new TextAreaRenderer());
		table.getColumnModel().getColumn(1).setCellEditor(new TextAreaEditor());

		table.getModel().addTableModelListener(new TableModelListener() {
			public void tableChanged(TableModelEvent evt) {
				for (int row = evt.getFirstRow(); row <= evt.getLastRow(); row++) {
					calculateAndSetRowHeigth();
				}
			}
		});

		table.addComponentListener(new ComponentListener() {
			public void componentHidden(ComponentEvent e) {}
			public void componentMoved(ComponentEvent e) {}

			public void componentResized(ComponentEvent e) {
				calculateAndSetRowHeigth();
			}

			public void componentShown(ComponentEvent e) {
				calculateAndSetRowHeigth();
			}
		});
	}

	private void calculateAndSetRowHeigth() {
		int width = table.getColumnModel().getColumn(1).getWidth();
		for (int row = 0; row < table.getRowCount(); row++) {
			JTextArea area = new JTextArea();
			area.setLineWrap(true);
			area.setWrapStyleWord(true);
			area.setSize(width, Short.MAX_VALUE);
			area.setText(table.getValueAt(row, 1).toString());
			System.out.println(area.getPreferredSize().height);
			table.setRowHeight(row, area.getPreferredSize().height);
		}
	}

	class TextAreaEditor extends AbstractCellEditor implements TableCellEditor {

		private JTextArea area;
		private JScrollPane pane;

		public TextAreaEditor() {
			area = new JTextArea();
			area.setLineWrap(true);
			area.setWrapStyleWord(true);
			area.setBorder(BorderFactory.createLineBorder(Color.BLACK));
			pane = new JScrollPane(area);
		}

		public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
			if (value != null)
				area.setText(value.toString());
			else
				area.setText("");
			return pane;
		}

		public Object getCellEditorValue() {
			return area.getText();
		}
		
		public void specialActivate() {
			area.requestFocusInWindow();
		}
	}

	class TextAreaRenderer extends DefaultTableCellRenderer {
		private JTextArea area;

		public TextAreaRenderer() {
			area = new JTextArea();
			area.setLineWrap(true);
			area.setWrapStyleWord(true);
		}

		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
			Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
			area.setText(value.toString());
			area.setForeground(c.getForeground());
			area.setBackground(c.getBackground());
			if (hasFocus)
				area.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
			else
				area.setBorder(null);
			return area;
		}
	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				final JFrame frame = new TextAreaRendererDemo();
				frame.setBounds(0, 0, 500, 300);
				frame.setLocationRelativeTo(null);
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setVisible(true);
			}
		});
	}
}
 
Mildert folgendes diesen Effekt?

Tatsächlich! - Das hat Abhilfe geschaffen ...

Ich muss aber auch sagen; Manchmal hab ich schon in Foren gepostet und als ich dann die Antworten gelesen habe, dachte ich mir nur "Okay, das hättest du jetzt auch hinkriegen können, wenn du dich richtig reingehängt hättest..."

Und jetzt erleb ich grad das komplette Gegenteil - auf die Ansätze wäre ich erst relativ spät gekommen!
Auf jeden fall hat mich diese relativ überschaubare Swing-Aufgabe schon kurzzeitig glauben
lassen, dass ich irgendwie meinen Beruf verfehlt hätte.
Ich halte es für ein sehr komplexes/sensibles Themen-Feld ...

Vielen Dank für die kompetenten Antworten!
 

Zurück
Oben