Search code examples
javaswingawtlayout-managerborder-layout

Swing components are not visible in custom layout manager


I've made the following layout manager class:

public class MainFrameLayout extends BorderLayout
{
    private final JPanel north, center, south;

    /**
     * Constructor for this layout.
     */
    public MainFrameLayout()
    {
        super();

        north = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
        center = new JPanel();
        center.setLayout(new BoxLayout(center, BoxLayout.Y_AXIS));
        south = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));

        north.setVisible(true);
        center.setVisible(true);
        south.setVisible(true);

        super.addLayoutComponent(north, NORTH);
        super.addLayoutComponent(center, CENTER);
        super.addLayoutComponent(south, SOUTH);
    }

    @Override
    public void addLayoutComponent(Component comp, Object constraints)
    {
        if (!(constraints instanceof MainFrameLayoutConstraints))
            throw new IllegalArgumentException("Invalid constraints");

        switch ((MainFrameLayoutConstraints) constraints)
        {
            case NORTH:
                north.add(comp);
                break;

            case CENTER:
                center.add(comp);
                break;

            case SOUTH:
                south.add(comp);
                break;
        }
    }
}

MainFrameLayoutConstraints is a generic enum class with only NORTH, CENTER, and SOUTH variants.

I attempted to use this layout in the following application:

public class MyApplication extends JFrame
{
    private final JFormattedTextField caseNumberBox;

    public MyApplication()
    {
        super("A Title Thingy");
        this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);

        NumberFormat caseNumberFormat = NumberFormat.getIntegerInstance();
        caseNumberBox = new JFormattedTextField(caseNumberFormat);
        caseNumberBox.setColumns(20);

        this.setLayout(new MainFrameLayout());
        this.add(new JLabel("Major Release Case: "), MainFrameLayoutConstraints.NORTH);
        this.add(caseNumberBox, MainFrameLayoutConstraints.NORTH);

        this.pack();
        this.setVisible(true);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        MyApplication app = new MyApplication();
    }
}

Why, when I run this application, are my components (the label and text field) invisible even though the call to pack() sized the window appropriately to fit those fields?


Solution

  • The reason for the behaviour is trying to create components inside the layout manager. While MainFrameLayout calls super.addLayoutComponent() for the components it creates, these are not added to the parent component itself. Therefore, the components you add to the frame count for the frame's preferred size calculation, as that's delegated to BorderLayout which assumes the content pane contains panels you created in the MainFrameLayout constructor, but they never get drawn as the panels haven't actually been added to the content pane.

    A custom layout manager is the wrong tool for what you're trying to achieve. Simply use a nested layout.