views:

155

answers:

3

Is there a widget for Swing that behaves like a JLabel, that automatically reflows the text if its dimensions have changed? For example:

Large horizontal space available:
+--------------+
| Foo bar baz  |
+--------------+

Small horizontal space available:
+---------+
| Foo bar |
| baz     |
+---------+

I am currently using JEditorPane with setContentType("text/html") and HTML content. This works, but it does not use the System's default label font for displaying the text. Also, I would rather not put HTML tags into my text - at best, \n characters would be transformed into line breaks and everything else would be displayed as text.

+2  A: 

I'm not sure of this, but I think it's worth a try:

You can set a JLabel's text using HTML. This should take care of the font issue. Simply do something like

lbl.setText("<html><body>Foo bar baz</body></html>");

See how the text behaves then.

If that works for you, you can override JLabel's setText() method to convert \n into <br/> and wrap html and body tags around the text.

Carl Smotricz
No need for <body> tag. Just having <html> is enough
eugener
+3  A: 

You can use JTextArea, a multi-line plain-text widget.

JavaDoc: http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JTextArea.html

 JTextArea ta = new JTextArea("A JTextArea containing a long text");
 ta.setLineWrap(true);
 ta.setWrapStyleWord(true);
 ta.setOpaque(false);
 ta.setEditable(false);
 ta.setFocusable(false);
  • setLineWrap(true) enables the wrapping
  • setWrapStyleWord(true) changes wrapping from character- to word-based
  • setOpaque(true) renders it opaque, like a normal label
  • setEditable(false) hides the caret and disables the user to change the text
  • setFocusable(false) disables the user to select the text

It look like a nornal JLabel, but it will wrap the text, whenever the width is too small.

Sven Lilienthal
At least in my case (Java 1.5 or Java 1.6), the line wrap does not behave as expected: it wraps the text at character level, not at word lebel. So in the above example, that could also produce something like "foo bar b(newline)az"
nd
@nd: You need to call 'setWrapStyleWord(true)' in order to get word-level line wrap.
uckelman
@uckleman: thanks, corrected the code
Sven Lilienthal
Oh, totally overlooked the wrapStyleWord property in the documentation. Thank you.
nd
If you have borders which are not completely opaque (e.g. a rounded border) you need to over-ride paintComponent() to prevent the background from painting in the insets area.
Justin
+1  A: 

I have made a custom UI delegate for JLabel to support multiple lines. It will wrap your text to fit the available space and also respect hard line breaks. The wrapped text should reflow when the component size changes. The UI delegate listens for changes to the component's dimension and recalculates the line breaks automatically.

Using the UI delegate is as straight forward as:

JLabel label = new JLabel("Text that'll wrap if necessary");
label.setUI(MultiLineLabelUI.labelUI);

Or alternatively use the custom MultiLineLabel class that in addition to wrapping text supports vertical and horizontal text alignment.

Here's the project (including a webstart demo): http://samuelsjoberg.com/archive/2009/10/multiline-labels-in-swing

If you try the demo, it should reflow fine on OS X. If I remember correctly there's some problem with reflowing upon resizing the frame on Windows. Didn't look in to it at the time, but it seemed that the resized event never propagated to the label.

Samuel Sjöberg
Very nice, that is even better than I wished for (w/ shadows)
nd
I can confirm there are problems on Windows, in my case I had to invoke `MLL.revalidate(); MLL.repaint();` in order to get long text to actually wrap without resizing the window. Problem seems to be that the MLL must be in a visible container before it works.
Justin