1

I'm trying put a JPanel (GridLayout 0, 1) with JButtons it, within the ViewPort of a JScrollPane. This is easy enough to do, but it doesn't behave "properly" when resizing the parent JFrame. Buttons resize to fill the entire panel as intended, but with one major limitation. If I attempt to resize the frame horizontally (in run-time by corner dragging), it stops after a point. The panel will extend infinitely, but it won't contract below the minimum size necessary to display the Text on the button with the shortest Text.

Resizing the frame vertically has no such issue. The frame will contract down to nothing. The buttons will squish until they hit their MinimumSize, after which point they simply clip outside the frame. This is the behaviour I want, because I intend to stick the whole thing inside a JScrollPane, at which point a scrollbar would appear. I want the same behaviour horizontally, but I can't get it no matter what I try.

So far, I've tried using GridLayout and GridBagLayout. The latter seems to be needlessly heavy-handed for just a single column of buttons. However, because this is a column, I can't use FlowLayout as that arranges objects laterally and I don't know how to change it. More to the point - both GridLayout and GridBagLayout full space and justify buttons in a way that I like, which I haven't been able to accomplish with other layout managers.

Major edit

This issue has taken me to so many dead ends that I've decided to clip the individual stream of consciousness in favour of unified summary of what happened. The relevant example code looks like this:

public static void main(String[] args)
{
    JFrame frame = new JFrame();
    frame.setSize(500, 500);
    frame.setLocation(200, 200);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    JButton button1 = new JButton("Button 1");
    JButton button2 = new JButton("------Button 2------");
    JButton button3 = new JButton("Button 3");
    
    JPanel buttonPanel = new JPanel(new GridLayout(0, 1));
    buttonPanel.add(button1);
    buttonPanel.add(button2);
    buttonPanel.add(button3);
    
    JButton button7 = new JButton("Button 1");
    JButton button8 = new JButton("------Button 2------");
    JButton button9 = new JButton("Button 3");
    
    JPanel buttonPanel2 = new JPanel(new GridLayout(0, 1));
    buttonPanel2.add(button7);
    buttonPanel2.add(button8);
    buttonPanel2.add(button9);
    
    JScrollPane scrollPaneA = new JScrollPane();
    scrollPaneA.getViewport().add(buttonPanel);
    scrollPaneA.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
    scrollPaneA.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    
    JScrollPane scrollPaneB = new JScrollPane();
    scrollPaneB.getViewport().add(buttonPanel2);
    scrollPaneB.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
    scrollPaneB.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    
    JTabbedPane tabbedPane = new JTabbedPane();
    tabbedPane.addTab("Left", scrollPaneA);
    tabbedPane.addTab("Right", scrollPaneB);
    
    JTextArea textArea = new JTextArea("Example Text");
    
    JScrollPane textScrollpane = new JScrollPane();
    textScrollpane.getViewport().add(textArea);
    textScrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
    textScrollpane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    
    JButton button4 = new JButton("Button 4");
    button4.addActionListener(new ButtonListener(tabbedPane, 0));
    JButton button5 = new JButton("------Button 5------");
    JButton button6 = new JButton("Button 6");
    button6.addActionListener(new ButtonListener(tabbedPane, 1));
    
    JPanel bottomPanel = new JPanel(new GridLayout(0, 1));
    bottomPanel.add(button4);
    bottomPanel.add(button5);
    bottomPanel.add(button6);
    
    JScrollPane rightScrollPane = new JScrollPane
    (
            bottomPanel,
            JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
            JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED
    );
    
    JSplitPane rightSplitPane = new JSplitPane
    (
            JSplitPane.VERTICAL_SPLIT,
            textScrollpane,
            rightScrollPane
    );
    rightSplitPane.setDividerLocation(100);
    
    JSplitPane mainSplitPane = new JSplitPane
    (
            JSplitPane.HORIZONTAL_SPLIT,
            tabbedPane,
            rightSplitPane
    );
    mainSplitPane.setDividerLocation(100);
    
    frame.add(mainSplitPane);
    frame.setVisible(true);
}

public class ButtonListener implements ActionListener
{
    private JTabbedPane tabbedPane;
    private int tab;

    public ButtonListener(JTabbedPane tabbedPane, int tab)
    {
        this.tabbedPane = tabbedPane;
        this.tab = tab;
    }

    @Override
    public void actionPerformed(ActionEvent e)
    {
        JButton button1 = new JButton("Button ?");
        JButton button2 = new JButton("------Button 2?------");
        JButton button3 = new JButton("Button ?");

        JPanel panel = new JPanel(new GridLayout(0, 1));
        panel.add(button1);
        panel.add(button2);
        panel.add(button3);

        this.tabbedPane.setComponentAt(this.tab, panel);
    }
}

This is essentially what I had in my programme. The JTabbedPane started out scrolling properly as one would expect. However, the moment I edited any of the button lists, scrolling broke. As you can see in the listener, I simply replace the entire panel associated with a tab. As soon as I did that, the JTabbedPane would stop scrolling.

I thought this was caused by the minimum size of the JButtons somehow overriding the JScrollPane's ability to scroll, hence the title of the question. While that was a factor, the actual root cause could be found in the ActionListener: this.tabbedPane.setComponentAt(this.tab, panel);

My design called for each tab to hold its own JScrollPane object, which held a JPanel within its Viewport. What I was actually doing was placing the JPanel directly into the tab. Of course it wouldn't scroll - I was orphaning the JScrollPane which was supposed to do the scrolling.

The solution was embarrassingly simple. I just fixed the tab replacement line to set the viewport on the JScrollPane, rather than replacing the Component in the tab. Essentially, this:

public void actionPerformed(ActionEvent e)
{
    JButton button1 = new JButton("Button ?");
    JButton button2 = new JButton("------Button 2?------");
    JButton button3 = new JButton("Button ?");
    
    JPanel panel = new JPanel(new GridLayout(0, 1));
    panel.add(button1);
    panel.add(button2);
    panel.add(button3);
    
    //this.tabbedPane.setComponentAt(this.tab, panel);
    JScrollPane scrollPane = (JScrollPane)this.tabbedPane.getComponentAt(tab);
    scrollPane.getViewport().add(panel);
}

And now it works exactly as intended. This was entirely my fault for getting lost in the structure, and forgetting that tabs are supposed to hold JScrollPanes, not JPanels.

5
  • I'm not understanding the question. The frame will resize horizontally no problem. When the frame is resize smaller than the preferred size of the buttons, then the scrollbar will appear. I tested on Window 11 using JDK11. Maybe this is a version/platform issue. Post an image showing what you see.
    – camickr
    Commented Jun 29 at 18:04
  • Note, a scroll pane respects the preferred size of the component added to the frame. So the scrollbars appear when the preferred size of the component added to the viewport of the scroll pane is greater than the size of the scroll pane. Setting the minimum size on the panel containing the buttons does affect the scrolling.
    – camickr
    Commented Jun 29 at 18:33
  • The problem is that the JScrollPane won't contract smaller than ~100px (in my example) no matter what preferred sizes I use. If I don't use any, or the PreferredSizes are smaller than 100, a horizontal scrollbar never appears. The JFrame is just prevented from resizing any smaller. Commented Jun 29 at 18:47
  • 1) Still not seeing an image of the problem. 2) You initially stated the frame will not size smaller than is needed to display the text on the buttons. A frame will only size so small because it needs to display the buttons in the title bar. Try using a undecorated frame to see the difference.
    – camickr
    Commented Jun 29 at 19:43
  • Oh! OK, so the JFrame is refusing to contract because of the title bar, not the contents. That makes sense, and makes me feel silly for not realising it. I tried recreating the same behaviour with a JSplitPane and that's able to go as far left as needed, with the scrollbar appearing as intended. I don't know if this necessarily solves my issue, but it gives me something to work with. I'll put together a larger example and ask for additional help if needed. Thank you. Commented Jun 30 at 0:13

2 Answers 2

1

I made a Java class with your code.

There's no need to size the individual JButtons The JPanel GridLayout will make them all the same size. I added some whitespace to the button panel so you could see the blue background.

Horizontally, I could shrink the JFrame to about 100 pixels. I could expand the JFrame to the width of my monitor.

Vertically, I could shrink the JFrame to the title bar. I could expand the JFrame to the height of my monitor.

I ran the code on Java 8.

Here's the complete runnable code.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;

public class JScrollPaneExample implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new JScrollPaneExample());
    }

    @Override
    public void run() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JTabbedPane tabbedPane = new JTabbedPane();
        JScrollPane scrollPane = new JScrollPane(createButtonPanel());
        tabbedPane.addTab("Tab ", scrollPane);
        frame.add(tabbedPane, BorderLayout.CENTER);

        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JPanel createButtonPanel() {
        JPanel panel1 = new JPanel(new GridLayout(0, 1, 5, 5));
        panel1.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        panel1.setMinimumSize(new Dimension(10, 10));
        panel1.setBackground(Color.BLUE);

        JButton button1 = new JButton("Button 1");
//      button1.setPreferredSize(new Dimension(300,100));
        panel1.add(button1);

        JButton button2 = new JButton("Button 2---------------");
//      button2.setPreferredSize(new Dimension(300,100));
        panel1.add(button2);

        JButton button3 = new JButton("Button 3");
//      button3.setPreferredSize(new Dimension(300,100));
        panel1.add(button3);

        JButton button4 = new JButton("But");
//      button4.setPreferredSize(new Dimension(300,100));
        panel1.add(button4);

        return panel1;
    }

}
3
  • PreferredSize for the buttons was just there to force a scrollbar, as part of a test. If I set the preferred size to anything above 100px or so, I can get them to appear. Otherwise, the JScrollPane stops contracting before the PreferredSize. The problem is that what you got is pretty similar to what I got. I can't contract the JScrollPane to less than 100px. And it's not exactly 100. It's however many is needed to contain the smaller Button names plus some padding (I think). Either that or there's some universal constant I don't know about. Is it possible to squish it to less than 100px? Commented Jun 29 at 18:43
  • @George Minkov: I couldn't. If you want the exact horizontal pixel count, create a container listener to print the JFrame size. Commented Jun 29 at 19:33
  • I edited the OP with some additional experimentation. It seems like my issue was the testing environment itself. Based on another comment, I attempted to replicate this behaviour using a JSplitPane and that had none of the undesirable behaviours. The issue was the minimum size of the JFrame title bar. Now to actually implement that into my programme proper. Thank you. Commented Jun 30 at 0:35
1

So, after wrestling with this issue for a few days and going down multiple leads which turned into dead ends, I finally found the issue at the root of it - and it was PEBKAC on my part. It wasn't the JFrame title bar, it wasn't the layout managers, it wasn't minimum size.

I was placing the JButtons inside a JPanel inside a JScrollPane inside the tabs of a JTabbedPane, which is inside one side of a JSplitPane. At certain events, the list of buttons was changed and the entire panel redrawn. In the process of rebuilding the JPanel, I was assigning that as the JSPlitPane's tab component. Of course it wouldn't scroll - I was orphaning the JSPlitPane and just using the panel directly.

Changing the ActionListener to properly assign the JSplitPane as a Component for the Tab fixed the issue entirely. And made me feel really silly for basic this mistake was...

6
  • Maybe you could edit your answer and post your corrected code because, at least for me, from your description it is not clear what changes you made in your code in order to solve the problem.
    – Abra
    Commented Jul 2 at 3:32
  • I'll see what I can do. Thing is, the problem turned out to be outside the example I'd posted. It wasn't an issue with JScrollPane or JPanel or GridLayout. I'll have to post a few excerpts to explain. Commented Jul 3 at 10:01
  • If the question is not related to your actual problem, maybe you should consider deleting the question.
    – Abra
    Commented Jul 3 at 11:24
  • Well, the issue I was having turned out to be largely unrelated to the question. You believe I should delete my own question in this case? That sounds fair. Can I just get confirmation that this is standard practice here? Commented Jul 3 at 17:55
  • Of-course it is standard practice. Alternatively, edit your question so that it describes your actual problem that you found a solution for.
    – Abra
    Commented Jul 3 at 18:05

Not the answer you're looking for? Browse other questions tagged or ask your own question.