HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Vertical JButton allignment

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
allignmentjbuttonvertical

Problem

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 (ClassNotFoundE

Solution

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 > 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();
           }
        }
    }
}

Code Snippets

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 > 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();
           }
        }
    }
}

Context

StackExchange Code Review Q#106043, answer score: 2

Revisions (0)

No revisions yet.