javaswingawtlayout-managerflowlayout

Unexpected behavior of FlowLayout


I'm using JPanel with a default FlowLayout layout manager. I think in case the application window was resized and there is not enough width to show all components in JPanel in one row, some components will be moved to another row(s).

This assumption is based on the documentation:

If the horizontal space in the container is too small to put all the components in one row, the FlowLayout class uses multiple rows.

https://docs.oracle.com/javase/tutorial/uiswing/layout/flow.html

It works in some cases. For example, while using this code and decreasing window width, buttons will be located on different rows:

import javax.swing.*;
import java.awt.*;

public class FlowLayoutExpected {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();

        JButton button1 = new JButton("Button 1");
        JButton button2 = new JButton("Button 2");
        JButton button3 = new JButton("Button 3");

        panel.add(button1);
        panel.add(button2);
        panel.add(button3);

        frame.getContentPane().add(BorderLayout.CENTER, panel);

        frame.setSize(350, 70);
        frame.setVisible(true);
    }
}

Before decreasing width -> After decreasing width

But it is enough to put one JPanel into another JPanel and layout manager stops to behave as expected:

import javax.swing.*;
import java.awt.*;

public class FlowLayoutReal {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JPanel outerPanel = new JPanel();
        JPanel innerPanel = new JPanel();

        JButton button1 = new JButton("Button 1");
        JButton button2 = new JButton("Button 2");
        JButton button3 = new JButton("Button 3");

        innerPanel.add(button1);
        innerPanel.add(button2);
        innerPanel.add(button3);

        outerPanel.add(innerPanel);
        frame.getContentPane().add(BorderLayout.CENTER, outerPanel);

        frame.setSize(350, 70);
        frame.setVisible(true);
    }
}

Before decreasing width -> After decreasing width

I'm trying to clarify what I misunderstood.


I've read a lot of suggestions to use WrapLayout instead of FlowLayout. It seems reasonable, but still not clear for me why I got inconsistent behavior in the examples above.


Solution

  • frame.getContentPane().add(BorderLayout.CENTER, panel);
    

    First of all that method has been "obsolete" since JDK1.1.

    The preferred approach since then is to add the "constraint" as the second parameter:

    frame.getContentPane().add(panel, BorderLayout.CENTER);
    

    But it is enough to put one JPanel into another JPanel and layout manager stops to behave as expected:

    The code is working as expected. The issue is your expectation.

    Change the code in your first example:

    //frame.getContentPane().add(panel, BorderLayout.CENTER);
    frame.getContentPane().add(panel, BorderLayout.PAGE_START);
    

    Now, when you decrease the width the components disappear. This is because the BorderLayout.PAGE_START will respect the preferred height of the component added. The preferred height is determined by displaying all components on a single row.

    But it is enough to put one JPanel into another JPanel and layout manager stops to behave as expected

    When you start wrapping panels you need to understand the implications.

    The FlowLayout respects the preferred size of all components added. So the components in the inner panel are all displayed in a row as expected.

    When you add the outer panel to the BorderLayout.CENTER the outer panel size is adjusted, but it does not affect the inner panel because the rules of the FlowLayout, say any component added is displayed at the top of the panel. Since the inner panel is a single component, there is nothing to wrap.

    This is a trick that is often used to your advantage to ensure a component retains its preferred size.

    Another example. Maybe you have a horizontal GridLayout of buttons on a panel you want to add to the BorderLayout.PAGE_START.

    If you add the panel directly, then the size of the button will shrink/grow as the frame with is changed.

    If you want the buttons size to remain constant, you can use a "wrapper" panel. Then the "wrapper" panel size shrinks/grows, but the buttons size remains constant.

    You need to understand the rules of the layout manager so you can use it effectively.