Vertical JButton allignment I have created a simple component in Swing that lets me place other components such as buttons on top of each other.

They fill their parent horizontally but not vertically, which is exactly as I want it. I called this parent component VerticalPanel and this is how it looks:



I can't shake the feeling though that there could be a more elegant solution. Especially the need for my VerticalPanel to know the number of components it will hold in advance is something I'm not entirely happy with.

Here's the VerticalPanel:

public class VerticalPanel extends JPanel {
    private static final long serialVersionUID = 1L;

    public VerticalPanel(int nrOfComps) {
        LayoutManager layout = getVerticalLayout(nrOfComps);
        setLayout(layout);
    }

    @Override
    public Component add(Component comp) {
        return add(comp, getComponentCount());
    }

    @Override
    public Component add(Component comp, int index) {
        final GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        // Allocate extra width to the buttons
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.NORTH;
        gbc.gridx = 0;
        gbc.gridy = index;
        add(comp, gbc);
        return comp;
    }

    private LayoutManager getVerticalLayout(int nrOfComps) {
        GridBagLayout layout = new GridBagLayout();

        double[] rowWeights = new double[nrOfComps];
        rowWeights[nrOfComps - 1] = Double.MIN_VALUE;
        layout.rowWeights = rowWeights;
        return layout;
    }
}


This creates the example from the screenshot above:

public class VerticalButtons {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager
                            .getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException
                        | IllegalAccessException
                        | UnsupportedLookAndFeelException e) {
                    e.printStackTrace();
                }
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame();

        frame.getContentPane().add(createVerticalButtons());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

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

    private static JComponent createVerticalButtons() {

        VerticalPanel buttons = new VerticalPanel(2);

        JButton b1 = new JButton("1");
        JButton b2 = new JButton("2");
        buttons.add(b1);
        buttons.add(b2);

        return buttons;
    }
}
as I mentioned in my comment I was playing with a LayoutManager. Based off of what you are hoping to achieve I would suggest going down this path as you are trying to control how the components are laid out which is what the LayoutManager does. Here is a version that works, but could stand to be cleaned up a bit more, and adapted a tad more to cover more scenario's (padding, margins, isVisible...etc)

public class VerticalLayout implements LayoutManager {
    private final Dimension minimumSize = new Dimension();

    @Override
    public void addLayoutComponent(String name, Component comp) {
    }

    @Override
    public void removeLayoutComponent(Component comp) {
    }

    @Override
    public Dimension preferredLayoutSize(Container parent) {
        Dimension dimension = new Dimension(getTotalPaddingWidth(parent), getTotalPaddingHeight(parent));
        minimumSize.height = dimension.height;
        for(Component c :parent.getComponents()){
            if(!c.isVisible()){
                continue;
            }

            Dimension preferredSize = c.getPreferredSize();
            dimension.height += preferredSize.getHeight();
            if(preferredSize.width &gt; dimension.width){
                dimension.width = preferredSize.width;
            }
        }
        minimumSize.width = dimension.width;
        return dimension;
    }

    private int getTotalPaddingWidth(Container container) {
        Insets padding = container.getInsets();
        return padding.left + padding.right;
    }
    private int getTotalPaddingHeight(Container container){
        Insets padding = container.getInsets();
        return padding.top + padding.bottom;
    }
    @Override
    public Dimension minimumLayoutSize(Container parent) {
        return minimumSize;
    }

    @Override
    public void layoutContainer(Container parent) {
        Insets padding = parent.getInsets();
        int y = padding.top;
        for(Component c : parent.getComponents()){
            if(c.isVisible()) {
                c.setBounds(padding.left, y, parent.getWidth(), c.getPreferredSize().height);
                y += c.getHeight();
           }
        }
    }
}