0

I have a JScrollPane with a number of JLabel objects in a panel using a GridBagLayout. Each of the labels is displaying HTML text with rich elements which varies at run time.

I would like all labels to have the same width (driven by the width of the scroll pane) but vary in height depending on their content with the text wrapping (as is handled automatically by JLabel). If the labels exceed the scroll pane's height then a vertical scroll bar should appear.

Here is some sample code to demonstrate the problem:

public class ScrollLabels extends JFrame {
    private final JPanel labelPanel = new JPanel(new GridBagLayout());
    private final GridBagConstraints c = new GridBagConstraints();

    public ScrollLabels() throws HeadlessException {
        super("Scroll Labels");
    }

    public void createUI() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JScrollPane scroller = new JScrollPane(labelPanel);
        add(scroller);
        c.gridx = 0;
        c.gridy = GridBagConstraints.RELATIVE;
        c.fill = GridBagConstraints.HORIZONTAL;
        addLabel("Here is <em>Rich Text</em>");
        addLabel("Here is <ul><li>A</li><li>List</li></ul>");
        addLabel("Here is <table><tr><th>A</th><th>Table></th></tr></table");
        addLabel("Here is more <em>Rich Text</em>");
        addLabel("Here is even more <b>Rich Text</b>");
        addLabel("Here is a long sentence that should wrap when the panel "
                + "is too small for the text.");
        pack();
    }

    private void addLabel(String text) {
        JLabel label = new JLabel("<html>" + text + "</html>");
        label.setBorder(BorderFactory.createEtchedBorder());
        labelPanel.add(label, c);
    }

    public static void main(String[] args) {
        ScrollLabels frame = new ScrollLabels();
        frame.createUI();
        frame.setVisible(true);
    }    
}

It correctly resizes the labels horizontally and shows scroll bars where appropriate. What it doesn't do is resize labels vertically to fit them within the scroll pane.

Here are the various things I have tried:

  • Changing the GridBagConstraint values. There are good controls for how to expand and contract components but I can't see any way to set a min or max width.
  • Setting the JScrollPane scroll bar policy to never show horizontal scroll bars. This just cuts off the label text rather than wrapping the text.
  • Manually setting the label size - i.e. setting the width from the scroll pane and the height depending on the text. I can't see an easy way to get the correct height of rich HTML text given a fixed width. In any case I'd prefer to have a layout manager that can do the job rather than manually coding preferred sizes.

The one thing I haven't tried yet is creating a custom layout manager. I suspect this might be the right answer but would like to see if any of you have an easier solution that I'm not seeing.

sprinter
  • 24,103
  • 5
  • 40
  • 71
  • 1
    *"a number of `JLabel` objects in a panel using a `GridBagLayout`. Each of the labels is displaying HTML text"* Not sure if it will work for this use-case, but I'd typically specify a width using styles. E.G. as shown in [this answer](https://stackoverflow.com/a/7861833/418556). – Andrew Thompson Aug 04 '17 at 03:04

1 Answers1

2

I would like all labels to have the same width (driven by the width of the scroll pane) but vary in height depending on their content

You need to implement the Scrollable interface on your panel and override the getScrollableTracksViewportWidth() method to return true. You will also need to provide default implementations for the other methods of the interface.

Or you can use the Scrollable Panel which provides method that allow you to set the scrolling properties.

camickr
  • 308,339
  • 18
  • 152
  • 272
  • Thanks for your help I wasn't aware of Scrollable. That seems to be the right approach. I still have an issue that the panel jumps to minimum width if any wrapping is required. Have you seen that issue before? If not I'll do some digging and post a new question if I can't figure it out. – sprinter Aug 04 '17 at 03:30
  • @sprinter, it is the solution for sizing the panel to fit the viewport. `I still have an issue that the panel jumps to minimum width` - no the panel width doesn't change. The size of the label on the panel may change. That is how the GridBagLayout works. If the size of the component can't fit in the area provided then the minimum size of the component is used. Maybe you can use a custom JLabel and override the `getMinimumSize()` method to make the width the size of the parent container? – camickr Aug 04 '17 at 14:10