How do I center a JPanel containing a FlowLayout?

3.4k views Asked by At

I have a JPanel containing a few components, laying them out with a FlowLayout, so that they will wrap if necessary and i want to center this JPanel in a JFrame. I tried to use a BoxLayout with a VerticalGlue, but it doesn't center it completely. Here is a simple SSCCE, which demonstrates my problem, because i dont think i can explain it well:

import javax.swing.*;
public class Test extends JFrame {
    public static void main(String[] args) {
        new Test();
    }
    public Test() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
        getContentPane().add(Box.createVerticalGlue());
        JPanel panel = new JPanel();
        panel.add(new JButton("New button"));
        panel.add(new JButton("New button"));
        getContentPane().add(panel);
        setVisible(true);
    }
}

If the window is wide enough to fit both buttons next to each other, it almost seems centered:

But if its too small you can clearly see my problem:

The problem is that the JPanel takes the lower half of the JFrame and the Glue the upper half:

I have tried other LayoutManagers aswell, but I couldn't fix this problem yet.

EDIT:

With a BorderLayout and the JPanel placed in the center the buttons appear at the top because the JPanel expands to the size of the ContentPane of the JFrame:

With a GridBagLayout it looks perfect with everything next to each other. But if the components are wrapped they disappear:

Using a MigLayout, the buttons wont wrap anymore:

EDIT: The solution i used is a combination of the Wrap Layout with the GridBagLayout:

import java.awt.*;
import javax.swing.*;
class Test extends JFrame {
    public static void main(String[] args) {
        new Test();
    }
    Test() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        GridBagLayout gridBagLayout = new GridBagLayout();
        gridBagLayout.columnWeights = new double[]{1.0};
        getContentPane().setLayout(gridBagLayout);
        JPanel panel = new JPanel(new WrapLayout());
        GridBagConstraints gbc_panel = new GridBagConstraints();
        gbc_panel.fill = GridBagConstraints.HORIZONTAL;
        getContentPane().add(panel, gbc_panel);
        panel.add(new JButton("New button"));
        panel.add(new JButton("New button"));
        setVisible(true);
    }
}
3

There are 3 answers

1
camickr On BEST ANSWER

The problem is that the JPanel takes the lower half of the JFrame and the Glue the upper half:

The problem is that the BoxLayout will allow components to grow up to the maximum size of the component. In the case of a panel, the maximum size is the maximum Integer value so both the glue and the panel get equal amounts of space. So if you want to use a BoxLayout, you need to override the getMaximumSize() method to return the preferred size of the panel.

I tried to use a BoxLayout with a VerticalGlue, but it doesn't center it completely.

After the above change you then need to use verticalGlue twice:

Box box = new Box.createVerticalBox(); 
box.add( Box.createVerticalGlue() );
box.add( panel );
box.add( Box.createVerticalGlue() );

Now you will have equal amounts of space above and below the panel. However, this still won't work when components wrap to the next line because the FlowLayout does not recalculate the height based on the wrapped components. So in this case you can replace the FlowLayout with the Wrap Layout which simply extends FlowLayout to calculate the wrapped height correctly.

Or instead of using a BoxLayout, you can use a GridBagLayout. By default components added to the GridBagLayout will be centered horizontally and vertically. You would still need the panel with the WrapLayout

0
cyber-monk On

Here is an example using the WrapLayout mentioned by camickr. I had to mess with max size of the wrap panel which is a bit of a hack, but I think it's enough to get you pointed in the right direction.

public class Test extends JFrame {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setLayout(new BorderLayout());

        JPanel boxPanel = new JPanel();
        boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS));
        boxPanel.setBorder(BorderFactory.createLineBorder(Color.GREEN, 1));

        JPanel wrapPanel = new JPanel();

        // hack of panel max size
        wrapPanel.setMaximumSize(new Dimension(99999, 0));
        wrapPanel.setBorder(BorderFactory.createLineBorder(Color.RED, 1));
        wrapPanel.setLayout(new WrapLayout(FlowLayout.CENTER));
        wrapPanel.add(new JButton("New button"));
        wrapPanel.add(new JButton("New button"));

        boxPanel.add(Box.createGlue());     
        boxPanel.add(wrapPanel);
        boxPanel.add(Box.createGlue());     

        add(boxPanel, BorderLayout.CENTER);

        setVisible(true);
    }
}
1
Programmer On

You can center a JPanel containing a FlowLayout by this code

setLayout(new FlowLayout(FlowLayout.CENTER));