# jTextArea/jTextPanel Auto Höhe



## _dp (18. Mrz 2011)

Hallo

Seltsamerweise spuckt mir die Forensuche nichts aus und auch Google war bisher relativ nutzlos, da sich alle Ergebnisse eher auf Tabellenhöhe in Verbindung mit einer Textarea beliefen.

Geplant ist folgendes:
Ich habe eine Tabellendatenbank mit einer Menge an Einträgen.
Klicke ich eins dieser Einträge an, bekomme ich neben der Tabelle eine detailierte Ansicht mit Daten aus dem jeweiligen Eintrag. Darunter einige Labels, Bilddateien und eben ein Textfeld für größere Texte, das ganze befindet sich in einer JScrollPanel, damit man es bei Übergröße auch scrollen kann.
Diese ganzen Elemente sollen sich jedes mal, wenn ein Tabelleneintrag ausgewählt wird, mit den neuen Daten aktualisieren. Die Größe des Textes kann dabei beliebig variieren.

Problem:


Ich fand bisher keinen Weg, einem jTextPanel oder jTextArea Objekt (Beide probiert) mitzuteilen, dass es sich die Höhe selbst festlegen soll.
Zwar konnte ich die Höhe auf Short.MAX_VALUE stellen, somit würden aber alle darunterliegenden Elemente entweder verschoben oder übermalt.
Der Text wird anhand der Wort-Trennungen gewrappt, die Breite bleibt fix, nur die Höhe soll sich anpassen können und die darunterliegenden Elemente korrekt neu anordnen. Dies ließe sich ja jedes mal bei Änderung des Textfeldes manuell positionieren, aber eben die Texthöhe konnte ich nicht herausfinden, getPreferredSize ist nicht dynamisch sondern gibt auch nur das aus, was ich vorher ihm bei der Erstellung als Initialwert für die preferierte Größe gab.

Code:

```
// Right side of split pane: The Quest detail container
						{
							jQuestInfoPanel = new JPanel();
							GroupLayout jQuestInfoPanelLayout = new GroupLayout((JComponent)jQuestInfoPanel);
							jQuestInfoPanel.setLayout(jQuestInfoPanelLayout);
							jQuestInfoPanel.setBackground(new java.awt.Color(52,82,80));
							jQuestInfoPanel.setForeground(new java.awt.Color(255,255,255));
							jQuestInfoPanel.setMinimumSize(new java.awt.Dimension(300, -1));
							jQuestInfoPanel.setPreferredSize(new java.awt.Dimension(-1, -1));
							{
								jQuestInfoQuestName = new JLabel();
								jQuestInfoQuestName.setText("{QUESTNAME}");
								jQuestInfoQuestName.setFont(new java.awt.Font("Tahoma",1,12));
								jQuestInfoQuestName.setForeground(new java.awt.Color(243,239,201));
							}
							{
								jQuestInfoRepeatableIndicator = new JLabel();
								jQuestInfoRepeatableIndicator.setText("{REPEATABLE}");
								jQuestInfoRepeatableIndicator.setFont(new java.awt.Font("Tahoma",0,10));
								jQuestInfoRepeatableIndicator.setForeground(new java.awt.Color(236,244,246));
							}
							{
								jQuestInfoShortDescription = new JTextArea();
								jQuestInfoShortDescription.setText("{SHORTDESCRIPTION}");
								jQuestInfoShortDescription.setBackground(new java.awt.Color(52,82,80));
								jQuestInfoShortDescription.setForeground(new java.awt.Color(255,255,255));
								jQuestInfoShortDescription.setFont(new java.awt.Font("Tahoma",0,12));
								jQuestInfoShortDescription.setWrapStyleWord(true);
								jQuestInfoShortDescription.setLineWrap(true);
								jQuestInfoShortDescription.setEditable(false);
							}

							jQuestInfoPanelLayout.setHorizontalGroup(jQuestInfoPanelLayout.createSequentialGroup()
								.addContainerGap()
								.addGroup(jQuestInfoPanelLayout.createParallelGroup()
								    .addComponent(jQuestInfoShortDescription, GroupLayout.Alignment.LEADING, 0, 276, Short.MAX_VALUE)
								    .addGroup(jQuestInfoPanelLayout.createSequentialGroup()
								        .addComponent(jQuestInfoRepeatableIndicator, GroupLayout.PREFERRED_SIZE, 277, GroupLayout.PREFERRED_SIZE)
								        .addGap(0, 0, Short.MAX_VALUE))
								    .addGroup(jQuestInfoPanelLayout.createSequentialGroup()
								        .addComponent(jQuestInfoQuestName, GroupLayout.PREFERRED_SIZE, 277, GroupLayout.PREFERRED_SIZE)
								        .addGap(0, 0, Short.MAX_VALUE)))
								.addContainerGap());
							jQuestInfoPanelLayout.setVerticalGroup(jQuestInfoPanelLayout.createSequentialGroup()
								.addContainerGap()
								.addComponent(jQuestInfoQuestName, GroupLayout.PREFERRED_SIZE, 23, GroupLayout.PREFERRED_SIZE)
								.addComponent(jQuestInfoRepeatableIndicator, GroupLayout.PREFERRED_SIZE, 16, GroupLayout.PREFERRED_SIZE)
								.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
								.addComponent(jQuestInfoShortDescription, GroupLayout.PREFERRED_SIZE, 22, GroupLayout.PREFERRED_SIZE)
								.addContainerGap(441, Short.MAX_VALUE));
						}
```

Testweise die Textarea mit Inhalt befüllt:

```
jQuestInfoShortDescription.setText("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.");
```

Hätte jemand hier ein paar Hilfestellungen für mich?


----------



## diggaa1984 (18. Mrz 2011)

ich würd ja eher sagen .. lass die höhen fix und arbeite auch da mit scrollen .. ich finde persönlich sich ständig ändernde komponenten (also layout und größe betreffend) eher unschön. der screenshot ist leider nich aussagekräftig in der hinsicht ^^ 

für mich sieht es aus als solle sich das textfeld an der linken komponente orientieren was die höhe angeht, da diese in einer scrollpane hängt, sollte die höhe des scrollpanes ja fix bleiben.

du kannst ja auch innerhalb eines textfeldes mit verschiedenen schriftstilen arbeiten


----------



## _dp (18. Mrz 2011)

Hmm, versteh ich nicht ganz wie das mein Problem lösen soll...

Also im Endeffekt sieht das so aus:
Ich habe eine bestimmte Anzahl an Elementen, die sich aus Labeln, Textfeldern und Bildern zusammensetzen. Die müssen in der türkisen Box untereinander ausgegeben werden.
Da je nach Eintrag die Anzahl/Größe der Elemente unterschiedlich ist, es also auch mal sein kann dass es keine Bilder oder kein Text geben kann, muss das ganze natürlich dynamsich sein was Höhe und Position der Elemente betrifft. Soweit stimmst du mir zu?

Wie ich das jetzt löse ist mir egal, ob TextArea oder TextPane, ob scrollbalken nord oder süd, total wurscht. Wäre erstmal ein Fortschritt wenn es überhaupt klappt 

Ich versuchs mal anderst. Hier ein kompletter Screenshot der UI:


Links: Tabelle mit Einträgen.
OnClick: Fülle rechten Info-Abschnitt mit Daten aus aktuellem Eintrag auf und zeige an

Gräulich angedeutet sind mögliche Textfelder variabler Höhe, grün angedeutet ein optionales Feld mit einer beliebigen Anzahl von kleinen Bildicons.
Pink: Hier könnte ein Scrollbalken auftauchen für den Fall, dass das ganze angezeigte zu groß ist.
Das Problem wird ja denke ich ersichtlich sein: Der Text und meine Fehlende Kompetenz die ganzen Komponenten in der Höhe dynamisch deren Inhalt anzupassen 

Dafür ist ein Problem gesucht, details sind eher unwichtig.


----------



## diggaa1984 (18. Mrz 2011)

dann werf ich dir einfach mal ein JEditorPane als Komponente hin, dort könntest du dann mit HTML-tags arbeiten und das quasi als HTML codiert anzeigen lassen (texte, bilder, styles ...)

das anordnen übernimmt dann die komponente


----------



## _dp (19. Mrz 2011)

Werd ich mir mal anschauen.
Bis jetzt ist dieses document und style-gewusel zwar ein totaler Krampf, aber muss zum Glück ja nur einmal gemacht werden.


----------



## André Uhres (19. Mrz 2011)

Hallo _dp,

versuch's mal so:


```
import java.awt.*;
import java.util.logging.*;
import javax.swing.*;
import javax.swing.text.*;
import static javax.swing.ScrollPaneConstants.*;

public class DetailPanelDemo {

    private final JFrame window;
    private final int DETAIL_WIDTH = 130;

    public DetailPanelDemo() {
        window = new JFrame("DetailPanelDemo");
        JPanel detailPanel = new JPanel();
        window.add(new JScrollPane(detailPanel,
                VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_NEVER),
                BorderLayout.LINE_END);
        window.setSize(300, 170);
        window.setLocationRelativeTo(null);
        window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        window.setVisible(true);
        showDetail(detailPanel);
    }

    private void showDetail(final JPanel panel) {
        //init
        initDetail(panel);
        //text1
        JTextPane textPane1 = new JTextPane();
        textPane1.setText("Some text Some text Some text Some text Some text Some text Some text ");
        textPane1.setEditable(false);
        panel.add(textPane1);
        //controls
        JPanel controls = new JPanel();
        controls.add(new JButton("1"));
        controls.add(new JButton("2"));
        panel.add(controls);
        //text2
        JTextPane textPane2 = new JTextPane();
        textPane2.setText("Some text Some text Some text Some text Some text Some text Some text ");
        textPane2.setEditable(false);
        panel.add(textPane2);
        //close
        closeDetail(panel);
    }

    private void initDetail(final JPanel panel) {
        panel.removeAll();
        panel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        panel.setBorder(BorderFactory.createLineBorder(Color.GRAY));
        panel.setPreferredSize(new Dimension(DETAIL_WIDTH, 0));
    }

    private void closeDetail(final JPanel panel) {
        final Component[] comps = panel.getComponents();
        int detailHeight = 0;
        for (final Component comp : comps) {
            if (comp instanceof JTextComponent) {
                JTextComponent textComp = (JTextComponent) comp;
                Dimension d1 = new Dimension(DETAIL_WIDTH - 4, 23);
                textComp.setSize(d1);
                Rectangle r1 = null;
                try {
                    r1 = textComp.modelToView(textComp.getDocument().getLength());
                } catch (final BadLocationException ex) {
                    Logger.getLogger(DetailPanelDemo.class.getName()).log(Level.SEVERE, null, ex);
                }
                d1.height = r1.y + r1.height;
                textComp.setPreferredSize(d1);
            }
            detailHeight += comp.getPreferredSize().height;
        }
        panel.setPreferredSize(new Dimension(panel.getPreferredSize().width,
                panel.getPreferredSize().height + detailHeight));
        window.validate();
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                JScrollPane scroller = (JScrollPane) SwingUtilities.getAncestorOfClass(
                        JScrollPane.class, panel);
                scroller.getVerticalScrollBar().setValue(0);
            }
        });
    }

    public static void main(final String... args) {
        Runnable gui = new Runnable() {

            public void run() {
                DetailPanelDemo demo = new DetailPanelDemo();
            }
        };
        // GUI must start on EventDispatchThread:
        SwingUtilities.invokeLater(gui);
    }
}
```
Zur Berechnung der Höhe des JTextPane bei konstanter Breite, benutzen wir die Methode JTextComponent#modelToView. Diese konvertiert die angegebene Stelle im Modell zu einer Stelle im Koordinatensystem der View. Damit diese Konvertierung funktioniert, muss die Textkomponente eine positive Größe haben (das Layout kann nicht berechnet werden, bis der Komponente eine Größe zugeordnet wurde). Die Komponente muss nicht sichtbar sein.

Gruß,
André


----------

