javaswinglayout-managergridbaglayoutflowlayout

Layout Manager assistance


I'm trying to make a collapsible panel look and having some issue with the layout manager, I'm pretty new at using layout managers and trying to userstand them. I have Jpanel with a flowlayout, I'm adding additional Jpanels which uses a GridBagLayout that removes and adds components with the click of a button, to give collapsed look to give it a form feel, I want the collapsible pane to stretch across the whole frame when I remove all the components but I get the following result enter image description here to look like this enter image description here which I achieved by adding a blank JLabel. This is working fine until I started adding more Jpanels that are not stretching across the screen like I want, like the Hardware Bundle Jpanel below.
enter image description here not 100% sure but I believe this is an issues with the Flow layout manager just doing its job and just adding the components to the frame, just didn't know if there is something I can do do get my collapse Jpanel to fill the frame horizontally.

Any help is appreciated

Here is my Jpanel class that uses the flowlayout and manages my background

public class Panel extends JPanel {

    private final ArrayList<Component> comp = new ArrayList();

    public Panel() {
        super();
        this.setLayout(new FlowLayout(FlowLayout.LEFT));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponents(g);
        g.drawImage(Assets.getImage(Assets.background, 350, 590), 0, 0, this);
    }

    public void addComp(Component c) {
        this.add(c);
        comp.add(c);   
    }
    
    public void createCopy(){
        Button copy = new Button("Process", new ImageIcon(Assets.getImage(Assets.button, 120, 35)));
        copy.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                for(Component c : comp){
                    System.out.println(((CollapsePane)c).format());
                }
            }
        });
        
        add(copy);
    }
}

here is my abstract class that manages the remove and adding of my form componenets

abstract class CollapsePane extends JPanel {

private JButton expand;
private JLabel fill = new JLabel("                                                                                                 ");
private boolean collapse = false;
public GridBagConstraints c;
private ArrayList<Pair> comp = new ArrayList();
String title;



public CollapsePane(String title) {
    this.setOpaque(false);
    this.title = title;
    TitledBorder border;
    border = BorderFactory.createTitledBorder(new BevelBorder(BevelBorder.RAISED, Color.black, Color.black), title);
    border.setTitleJustification(TitledBorder.LEFT);
    this.setBorder(border);

    this.setLayout(new GridBagLayout());
    c = new GridBagConstraints();
    c.anchor = GridBagConstraints.WEST;
    c.fill = GridBagConstraints.HORIZONTAL;
    c.gridx = 0;
    c.gridy = 0;
    createButton();
    this.add(expand, c);
   
    

}

public CollapsePane(String title, boolean collapse) {
    this.title = title;
    this.setOpaque(false);
    TitledBorder border;
    border = BorderFactory.createTitledBorder(new BevelBorder(BevelBorder.RAISED, Color.black, Color.black), title);
    border.setTitleJustification(TitledBorder.LEFT);
    this.setBorder(border);
    this.collapse = collapse;
    this.setLayout(new GridBagLayout());
    c = new GridBagConstraints();
    c.anchor = GridBagConstraints.WEST;
    c.fill = GridBagConstraints.HORIZONTAL;
    c.gridx = 0;
    c.gridy = 0;
    createButton();
    this.add(expand, c);
    this.collapse = collapse;
}



abstract void config();

private void createButton() {
    if (collapse == false) {
        expand = new Button("-", new ImageIcon(Assets.getImage(Assets.blankButton, 30, 30)));
    } else {
        expand = new Button("+" , new ImageIcon(Assets.getImage(Assets.blankButton, 30, 30)));
    }
    expand.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (collapse) {
                expand.setText("-");
                remove(fill);
                collapse = false;
                add();
            } else {
                expand.setText("+");
                clear();

                add(fill);
                collapse = true;

            }

        }
    });

}

private void clear() {
    System.out.println("clear");
    for (Pair p : comp) {
        this.remove((Component)p.getComponent());
    }

}

public void add() {
    //System.out.println("add");
    //System.out.println("compt size:" + comp.size());
   

    for (int i = 0; i < comp.size(); i++) {
        c.fill = GridBagConstraints.HORIZONTAL;
        c.anchor = GridBagConstraints.WEST;
        c.insets = new Insets(0, 5, 5, 5);
        c.gridx = (int)comp.get(i).getxPos();
        c.gridy = (int)comp.get(i).getyPos();
        // System.out.println("coor " + i + " " + x + " " + y);
        this.add((Component)comp.get(i).getComponent(), c);

    }
}

public void addComp(Component comp, int x, int y) {
    c.fill = GridBagConstraints.HORIZONTAL;
    c.anchor = GridBagConstraints.WEST;
    c.insets = new Insets(0, 5, 5, 5);
    c.gridx = x;
    c.gridy = y;
    if (!collapse) {
        this.add(comp, c);
    }
    this.comp.add(new Pair(comp,x,y));

}

Solution

  • Like most things, this is all about illusion. You also need to take into consideration each individual container and how they are getting laid out and how that might effect how the parent's get laid out.

    In the following example, the "contents" of the collapsed pane fill horizontally, but also occupy all the available horizontal space (ie weightx = 1), this ensures that the control is positioned to the "first start line" (ie top/left).

    However, when the "contents" are hidden (ie collapsed), the control button will become the only component used to determine the layout, meaning that it will shift to the middle of the container. Even if you layout of the container to occupy the horizontal area. This is perfectly normal.

    To over come this, I added a "filler" component that will, when the container is collapsed, occupy the remaining horizontal space instead (ie weightx = 1), there by forcing the control button to remain at the left side ... "magic"

    enter image description here

    import java.awt.Color;
    import java.awt.Component;
    import java.awt.EventQueue;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.Insets;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JRadioButton;
    import javax.swing.JTextField;
    import javax.swing.border.LineBorder;
    
    public class Test {
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
    
                    JPanel contentPane = new JPanel(new GridBagLayout());
                    GridBagConstraints gbc = new GridBagConstraints();
                    gbc.weightx = 1;
                    gbc.fill = GridBagConstraints.HORIZONTAL;
                    gbc.gridwidth = GridBagConstraints.REMAINDER;
                    gbc.insets = new Insets(2, 2, 2, 2);
    
                    CollapsablePane accountDetailsPane = new CollapsablePane();
                    accountDetailsPane.setContentPane(new AccountPane());
    
                    contentPane.add(accountDetailsPane, gbc);
    
                    CollapsablePane hardwarePane = new CollapsablePane();
                    hardwarePane.setContentPane(new HardwarePane());
    
                    contentPane.add(hardwarePane, gbc);
    
                    // Space filler
                    gbc.weighty = 1;
                    contentPane.add(new JPanel(new GridBagLayout()), gbc);
    
                    frame.add(contentPane);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class CollapsablePane extends JPanel {
    
            private JButton controlButton;
            private JLabel collapsedFiller;
            private JPanel contentPane = new JPanel();
    
            public CollapsablePane() {
                setLayout(new GridBagLayout());
                setContentPane(contentPane);
                setBorder(new LineBorder(Color.DARK_GRAY));
    
                controlButton = new JButton("-");
    
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.anchor = GridBagConstraints.FIRST_LINE_START;
    
                super.add(controlButton, gbc);
    
                gbc = new GridBagConstraints();
                gbc.gridx = 10;
                gbc.gridy = 0;
                gbc.weightx = 1;
                collapsedFiller = new JLabel();
                super.add(collapsedFiller, gbc);
                collapsedFiller.setVisible(false);
    
                controlButton.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        setCollapsed(getContentPane().isVisible());
                        controlButton.setText(getContentPane().isVisible() ? "-" : "+");
                    }
                });
            }
    
            public void setCollapsed(boolean collapsed) {
                JPanel contentPane = getContentPane();
                if (contentPane == null) {
                    return;
                }
                contentPane.setVisible(!collapsed);
                collapsedFiller.setVisible(collapsed);
            }
    
            public void setContentPane(JPanel newPane) {
                if (contentPane == newPane) {
                    return;
                }
                if (contentPane != null) {
                    remove(contentPane);
                }
                contentPane = newPane;
                if (contentPane == null) {
                    return;
                }
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 1;
                gbc.gridy = 1;
                gbc.weightx = 1;
                gbc.weighty = 1;
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.anchor = GridBagConstraints.FIRST_LINE_START;
                super.add(contentPane, gbc);
            }
    
            public JPanel getContentPane() {
                return contentPane;
            }
    
            @Override
            public Component add(Component comp) {
                throw new UnsupportedOperationException("Use setContentPane instead");
            }
    
            @Override
            public void add(Component comp, Object constraints) {
                throw new UnsupportedOperationException("Use setContentPane instead");
            }
    
            @Override
            public Component add(Component comp, int index) {
                throw new UnsupportedOperationException("Use setContentPane instead");
            }
    
            @Override
            public Component add(String name, Component comp) {
                throw new UnsupportedOperationException("Use setContentPane instead");
            }
    
            @Override
            public void add(Component comp, Object constraints, int index) {
                throw new UnsupportedOperationException("Use setContentPane instead");
            }
        }
    
        public class AccountPane extends JPanel {
            public AccountPane() {
                setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.anchor = GridBagConstraints.LINE_START;
    
                add(new JLabel("Title: "), gbc);
                gbc.gridy++;
                add(new JLabel("Manager: "), gbc);
                gbc.gridy++;
                add(new JLabel("Entity: "), gbc);
                gbc.gridy++;
                add(new JLabel("Location: "), gbc);
                gbc.gridy++;
                add(new JLabel("Mirror: "), gbc);
    
                gbc.gridy = 0;
                gbc.gridx = 1;
                gbc.fill = gbc.HORIZONTAL;
                gbc.weightx = 1;
    
                add(new JTextField(10), gbc);
                gbc.gridy++;
                add(new JTextField(10), gbc);
                gbc.gridy++;
                add(new JTextField(10), gbc);
                gbc.gridy++;
                add(new JTextField(10), gbc);
                gbc.gridy++;
                add(new JTextField(10), gbc);
            }
        }
    
        public class HardwarePane extends JPanel {
            public HardwarePane() {
                setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.anchor = GridBagConstraints.LINE_START;
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.weightx = 1;
    
                add(new JLabel("Hardware: "), gbc);
                gbc.gridy++;
                add(new JRadioButton("Hardware Bundle"), gbc);
                gbc.gridy++;
                add(new JRadioButton("Hardware Bundle with Verizon Card"), gbc);
    
                gbc.gridy++;
                add(new JLabel("Hardware Deployment: "), gbc);
                gbc.gridy++;
                add(new JTextField(10), gbc);
            }
        }
    }
    

    You will also note that I add a empty JPanel below the the two collapsed panes and force it to occupy the remaining vertical space, this ensures that the layout stays at the top of the container.