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
 

Haymaker84

Mitglied
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:

Michael...

Top Contributor
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);
	}
}
 

Haymaker84

Mitglied
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...
 

Michael...

Top Contributor
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);
			}
		});
	}
}
 

Haymaker84

Mitglied
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
 

Michael...

Top Contributor
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.
 

Haymaker84

Mitglied
[...] 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.
 

Michael...

Top Contributor
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);
			}
		});
	}
}
 

Haymaker84

Mitglied
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!
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
O No X11 DISPLAY Variable was set AWT, Swing, JavaFX & SWT 6
jojoge wie kann ich in eine Benennung eines JButtons eine Variable einbauen? AWT, Swing, JavaFX & SWT 6
S Swing jx cannot be resolved to a variable (2.) AWT, Swing, JavaFX & SWT 1
S Swing jx cannot be resolved to a variable AWT, Swing, JavaFX & SWT 6
S Swing Variable in Actionlistener aufrufen AWT, Swing, JavaFX & SWT 10
F Wie bekomme ich den Wert der ComboBox in eine Variable gespeichert welche ich für meinen ActionListener nutzen kann? AWT, Swing, JavaFX & SWT 3
S Java GUI durch variable Größe einer Map anpassen AWT, Swing, JavaFX & SWT 3
J jButton soll nach klicken eine Variable um 1 erhöhen AWT, Swing, JavaFX & SWT 2
G Swing Variable Elemente für GroupLayout AWT, Swing, JavaFX & SWT 18
MR._FIRE_Flower Variable setzten mit JButton AWT, Swing, JavaFX & SWT 5
P Variable einer Eingabe in anderes Textfeld schreiben AWT, Swing, JavaFX & SWT 7
Z ActionListener Variable übergeben AWT, Swing, JavaFX & SWT 12
K Wie kann ich eine Variable zwischen Tab Klassen weitergeben ? AWT, Swing, JavaFX & SWT 7
U Eingabe von TextField in variable speichern AWT, Swing, JavaFX & SWT 2
B SWT Globale Variable AWT, Swing, JavaFX & SWT 10
Z Swing Variable eines Objekt aus Hashmap in JList anzeigen AWT, Swing, JavaFX & SWT 1
J ActionListener erkennt Variable nicht AWT, Swing, JavaFX & SWT 6
D JavaFX Gesetzte Variable in einem Controller im Controller laden AWT, Swing, JavaFX & SWT 1
Z Wird die Variable nicht übergeben ? AWT, Swing, JavaFX & SWT 16
L Swing Variable Anzahl an JButtons AWT, Swing, JavaFX & SWT 7
L ActionPerformed Variable übergeben AWT, Swing, JavaFX & SWT 3
J Bilder als Variable an einem Objekt speichern AWT, Swing, JavaFX & SWT 3
J JavaFX Variable an neue Scene übergeben AWT, Swing, JavaFX & SWT 8
V Warum wird meine Variable nicht erkannt? AWT, Swing, JavaFX & SWT 2
M Swing Alter Wert von einer Variable wird verwendet AWT, Swing, JavaFX & SWT 8
F Swing TextField Eingabe in Variable umwandeln um zum Beispiel zu rechnen. AWT, Swing, JavaFX & SWT 3
Kandum obj_JMenu.addMenuListener - variable Anzahl an JMenus / menulistenern & frame.setTitle() dynamisch AWT, Swing, JavaFX & SWT 7
J Swing String soll als Variable dienen AWT, Swing, JavaFX & SWT 11
B Variable ist leer / Scope von Variablen AWT, Swing, JavaFX & SWT 2
J Button En-/Disablen - Warum variable final? AWT, Swing, JavaFX & SWT 2
M private Variable übergeben AWT, Swing, JavaFX & SWT 12
U AWT local variable * is accessed from within inner class; needs to be declared final AWT, Swing, JavaFX & SWT 6
R Swing Variable Panelbreite ohne Layoutmanager AWT, Swing, JavaFX & SWT 6
T Image skaliert in Variable speichern AWT, Swing, JavaFX & SWT 6
H Swing variable in Numberfield ausgeben AWT, Swing, JavaFX & SWT 5
C Swing JTextPane zeigt HTML-Text aus Variable nicht an :( AWT, Swing, JavaFX & SWT 3
B Anprechen von TextAreas mit Variable AWT, Swing, JavaFX & SWT 2
N Listener für Variable AWT, Swing, JavaFX & SWT 4
B Button benennen via Variable? AWT, Swing, JavaFX & SWT 6
B Variable wird nicht gefunden AWT, Swing, JavaFX & SWT 5
C Button/Textfeld mit Variable verknüpfen AWT, Swing, JavaFX & SWT 4
B Swing Variable in JTextfield geht nicht AWT, Swing, JavaFX & SWT 10
B problem mit variable (final) AWT, Swing, JavaFX & SWT 7
P Zeichen abhängig davon, ob sich eine Variable geändert hat AWT, Swing, JavaFX & SWT 2
T statische Variable zum Referenzieren einer Instanz von Frame AWT, Swing, JavaFX & SWT 3
J Integer aus Textdatei auslesen und in Variable speichern AWT, Swing, JavaFX & SWT 11
T Variable Menge an Bildern außerhalb paint() zeichnen? AWT, Swing, JavaFX & SWT 2
J -variable Formular Klasse AWT, Swing, JavaFX & SWT 2
E Variable übergeben AWT, Swing, JavaFX & SWT 4
B Cannot refer to a non-final variable (JButton) AWT, Swing, JavaFX & SWT 3
kb frame als globale variable AWT, Swing, JavaFX & SWT 2
G JTable - Zeilenhöhe variable? AWT, Swing, JavaFX & SWT 2
D Auf Variable zugreifen AWT, Swing, JavaFX & SWT 3
G variable übergeben in andere klasse AWT, Swing, JavaFX & SWT 4
S Bild in einer Variable speichern AWT, Swing, JavaFX & SWT 7
S Eingabe des Textfeldes in Variable speichern AWT, Swing, JavaFX & SWT 7
R JFrame -- Variable Positionierung durch Window Manager? AWT, Swing, JavaFX & SWT 2
M variable Jtable AWT, Swing, JavaFX & SWT 4
P GetSelectetItem() erkennt Variable aus Liste nicht. AWT, Swing, JavaFX & SWT 2
K Polygon in image variable? AWT, Swing, JavaFX & SWT 2
T Variable Zeilenhöhe in jTable AWT, Swing, JavaFX & SWT 5
P JTable variable Zeilengröße AWT, Swing, JavaFX & SWT 3
O Display Env. Variable unter (X11) von ClientApp. abfragen AWT, Swing, JavaFX & SWT 2
L Swing TextPanel Schrift untereinander und auf einer Höhe AWT, Swing, JavaFX & SWT 3
Jose05 Javafx Label Höhe=Breite AWT, Swing, JavaFX & SWT 1
sascha-sphw JavaFX ListCell höhe verändert sich beim ändern der Text-Farbe AWT, Swing, JavaFX & SWT 14
X JavaFX AreaChart area höhe wird nicht richtig dargestellt AWT, Swing, JavaFX & SWT 2
R Java FX - Fxml - relative Größenangaben für Breite und Höhe einer TextArea AWT, Swing, JavaFX & SWT 8
J JavaFX TableView - Höhe der Zeilen AWT, Swing, JavaFX & SWT 3
B JFreeChart Gantt Höhe Tasks AWT, Swing, JavaFX & SWT 1
Z Java Draw -JFrame finde nicht die Mitte & höhe, breite AWT, Swing, JavaFX & SWT 7
S SWT TableCell mit Composite als Inhalt / Höhe der Cell/Row AWT, Swing, JavaFX & SWT 5
T Swing JScrollPane in JPanel - Breite dynamisch, Höhe fix - wie? AWT, Swing, JavaFX & SWT 2
A LayoutManager Höhe von NORTH beim BorderLayout ändern AWT, Swing, JavaFX & SWT 4
M Wie ermittele ich die Breite und Höhe meines Displays in Pixel AWT, Swing, JavaFX & SWT 3
T LayoutManager GridBagLayout - zwei jTable mit unterschiedlicher Höhe AWT, Swing, JavaFX & SWT 2
N Swing Optimale Höhe eine JTextPanes bei festgelegter Breite bestimmen AWT, Swing, JavaFX & SWT 23
P Höhe der TitleBar vom JFrame AWT, Swing, JavaFX & SWT 2
Landei Swing JToolbar: Textfelder mit normaler Höhe? AWT, Swing, JavaFX & SWT 2
T jpanel höhe netbeans.... AWT, Swing, JavaFX & SWT 5
J Problem mit der Höhe eines Panels AWT, Swing, JavaFX & SWT 11
G Swing Höhe des View eines JScrollPane fest auf Höhe des JScrollPane setzen! AWT, Swing, JavaFX & SWT 4
H LayoutManager Auf gleicher Höhe anordnen AWT, Swing, JavaFX & SWT 4
_dp jTextArea/jTextPanel Auto Höhe AWT, Swing, JavaFX & SWT 5
D JPanel höhe setzen AWT, Swing, JavaFX & SWT 2
F Swing Höhe eines Headers einer JTable anpassen AWT, Swing, JavaFX & SWT 4
N Höhe einstellen JScrollPane AWT, Swing, JavaFX & SWT 3
F 2D-Grafik Problem mit höhe||breite eines JFrames AWT, Swing, JavaFX & SWT 4
G JTable Multiple Row Header Example - Probleme mit der Höhe der Zellen AWT, Swing, JavaFX & SWT 4
E Wie Breite und Höhe eines Panels festlegen? AWT, Swing, JavaFX & SWT 5
S Für Breite und Höhe werden 0.0 angezeigt AWT, Swing, JavaFX & SWT 2
C Swing JTextArea mit fester Breite und angepasster Höhe??? AWT, Swing, JavaFX & SWT 8
B Höhe eines Buttons festlegen AWT, Swing, JavaFX & SWT 7
G JList Höhe? AWT, Swing, JavaFX & SWT 2
P Breite und Höhe des contentPane abfragen AWT, Swing, JavaFX & SWT 3
Y Einzigartigkeit des GridBagLayout - individuelle Höhe/Breite AWT, Swing, JavaFX & SWT 4
S Tabelle soll sich auch in der Höhe resizen? AWT, Swing, JavaFX & SWT 4
T Breite des Containers ist gegeben - wie die Höhe berechnen? AWT, Swing, JavaFX & SWT 3
H JTabbedPane Höhe-Problem AWT, Swing, JavaFX & SWT 3
S [BorderLayout]WEST/EAST sollen über ganze Höhe gehen; AWT, Swing, JavaFX & SWT 3

Ähnliche Java Themen

Neue Themen


Oben