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):
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.
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):
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:
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...:
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
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