Search code examples
javaswingjpanellayout-managerboxlayout

Updating component's height also changes it's width


I have a GUI with one main JPanel and inside of it multiple rows, each row being another JPanel. Every row (of type JPanel) consists of 4 smaller JPanels and every panel out of those 4 has a component inside of it. The end result is a grid like interface.

Main panel has a BoxLayout and panels that are parts of a row have FlowLayout.

When I update height of some component (from row) using some listener, entire row becomes taller, which works as expected. But what happens is that not only height is changed, but also width of components (inside a row) is changed. I understand that BoxLayout is trying to layout the components using maxSize and minSize that I can set to be the same value and that worked, but then when I resize the window, other rows expand and the row with same minSize and maxSize doesn't and the grid structure becomes messed up.

What I want to achieve, is that I update only the height of the row. And when I resize the window, entire row expands, and the structure of grid is still the grid. Here is the Short, Self Contained, Correct (Compilable), Example:

Main class:

public class Main {

public static void main(String args[]) {

        SwingUtilities.invokeLater(() -> {

                new MainFrame(450,150);

            });

    }

}

MainFrame class:

public class MainFrame extends JFrame{

    public MainFrame(int width, int height) {

        super("Title");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(width, height);
        setVisible(true);

        JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
        JScrollPane scrollPane = new JScrollPane(mainPanel);

        add(scrollPane);


        for(int i=0; i<50; i++) {

            JPanel panel1 = new JPanel();
            panel1.setLayout(new FlowLayout(FlowLayout.LEFT));
            panel1.setBorder(BorderFactory.createMatteBorder(0, 1, 0, 1, Color.black));
            panel1.setPreferredSize(new Dimension(70,35));

            JPanel panel2 = new JPanel();
            panel2.setLayout(new FlowLayout(FlowLayout.LEFT));
            panel2.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.black));
            panel2.setPreferredSize(new Dimension(70,35));

            JPanel panel3 = new JPanel();
            panel3.setLayout(new FlowLayout(FlowLayout.LEFT));
            panel3.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.black));
            panel3.setPreferredSize(new Dimension(70,35));

            JTextArea area1 = new JTextArea("hello " + i);
            area1.setPreferredSize(new Dimension(70,25));
            panel1.add(area1);

            JTextArea area2 = new JTextArea("hello " + i);
            area2.setPreferredSize(new Dimension(70,25));
            panel2.add(area2);

            JTextArea area3 = new JTextArea("hello " + i);
            area3.setPreferredSize(new Dimension(70,25));
            panel3.add(area3);

            JPanel row = new JPanel();
            row.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.black));
            row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));

            row.add(panel1);
            row.add(panel2);
            row.add(panel3);

            JButton button = new JButton("Click me");
            JPanel buttonPanel = new JPanel();
            buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
            buttonPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.black));
            buttonPanel.setPreferredSize(new Dimension(70,35));
            buttonPanel.add(button);

            button.addActionListener(event -> {

                panel1.setPreferredSize(new Dimension(panel1.getWidth(), panel1.getHeight() + 30));
                area1.setPreferredSize(new Dimension(area1.getWidth(), area1.getHeight() + 30));
                area1.updateUI();
            });

            row.add(buttonPanel);
            mainPanel.add(row);
        }

    }

}

If you run this code and press button it will update not only row's height, but also row's width and grid is not aligned well anymore.


Solution

  • You are setting the "preferred size" based on the "size" of the component. The two can be different.

    Your code should be something like:

    //panel1.setPreferredSize(new Dimension(panel1.getWidth(), panel1.getHeight() + 30));
    Dimension d = panel1.getPreferredSize();
    panel1.setPreferredSize(new Dimension(d.width, d.height + 30));
    

    Also, you should not be using updateUI(). That is a method used internally by Swing on a LAF change.

    Instead, when you want to invoke the layout manager you invoke revalidate() on the top level component that was changed:

    //area1.updateUI();
    panel1.revalidate();