Too many JPanels inside a JPanel (with GridBagLayout)

后端 未结 3 1317
感动是毒
感动是毒 2020-12-04 03:25

So basically if I put JPanels inside a JPanel that uses GridBagLayout and I restrict the size with setPreferredSize, even

相关标签:
3条回答
  • 2020-12-04 03:48

    You have particular requirement which may be better served through the use of it's layout manager. This provides you the ability to control every aspect of the layout without the need to resort to hacks or "work arounds" which never quite work or have bizarre side effects

    public class AccordionLayout implements LayoutManager {
    
        // This "could" be controlled by constraints, but that would assume
        // that more then one component could be expanded at a time
        private Component expanded;
    
        public void setExpanded(Component expanded) {
            this.expanded = expanded;
        }
    
        public Component getExpanded() {
            return expanded;
        }
    
        @Override
        public void addLayoutComponent(String name, Component comp) {
        }
    
        @Override
        public void removeLayoutComponent(Component comp) {
        }
    
        @Override
        public Dimension preferredLayoutSize(Container parent) {
            Dimension size = minimumLayoutSize(parent);
            if (expanded != null) {
                size.height -= expanded.getMinimumSize().height;
                size.height += expanded.getPreferredSize().height;
            }
    
            return size;
        }
    
        @Override
        public Dimension minimumLayoutSize(Container parent) {
            int height = 0;
            int width = 0;
            for (Component comp : parent.getComponents()) {
                width = Math.max(width, comp.getPreferredSize().width);
                height += comp.getMinimumSize().height;
            }
            return new Dimension(width, height);
        }
    
        @Override
        public void layoutContainer(Container parent) {
    
            Insets insets = parent.getInsets();
            int availableHeight = parent.getHeight() - (insets.top + insets.bottom);
            int x = insets.left;
            int y = insets.top;
    
            int maxSize = 0;
            Dimension minSize = minimumLayoutSize(parent);
            if (expanded != null) {
                minSize.height -= expanded.getMinimumSize().height;
                // Try an honour the preferred size the expanded component...
                maxSize = Math.max(expanded.getPreferredSize().height, availableHeight - minSize.height);
            }
    
            int width = parent.getWidth() - (insets.left + insets.right);
            for (Component comp : parent.getComponents()) {
                if (expanded != comp) {
                    comp.setSize(width, comp.getMinimumSize().height);
                } else {
                    comp.setSize(width, maxSize);
                }
                comp.setLocation(x, y);
                y += comp.getHeight();
            }
    
        }
    
    }
    

    And the runnable example...

    This goes to the enth degree, creating a specialised component to act as each "fold", but this just reduces the complexity of the API from the outside, meaning, you just need to think about the title and the content and let the rest of the API take care of itself

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.FlowLayout;
    import java.awt.Insets;
    import java.awt.LayoutManager;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    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() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private AccordionLayout layout;
    
            public TestPane() {
                layout = new AccordionLayout();
                setLayout(layout);
    
                AccordionListener listener = new AccordionListener() {
                    @Override
                    public void accordionSelected(Component comp) {
                        layout.setExpanded(comp);
                        revalidate();
                        repaint();
                    }
                };
    
                Color colors[] = {Color.RED, Color.BLUE, Color.CYAN, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.YELLOW};
                String titles[] = {"Red", "Blue", "Cyan", "Green", "Magenta", "Orange", "Pink", "Yellow"};
                for (int index = 0; index < colors.length; index++) {
                    AccordionPanel panel = new AccordionPanel(titles[index], new ContentPane(colors[index]));
                    panel.setAccordionListener(listener);
                    add(panel);
                }
            }
    
        }
    
        public class ContentPane extends JPanel {
    
            public ContentPane(Color background) {
                setBackground(background);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(100, 100);
            }
    
        }
    
        public interface AccordionListener {
    
            public void accordionSelected(Component comp);
    
        }
    
        public class AccordionPanel extends JPanel {
    
            private JLabel title;
            private JPanel header;
            private Component content;
    
            private AccordionListener accordionListener;
    
            public AccordionPanel() {
                setLayout(new BorderLayout());
    
                title = new JLabel("Title");
    
                header = new JPanel(new FlowLayout(FlowLayout.LEADING));
                header.setBackground(Color.GRAY);
                header.setBorder(new LineBorder(Color.BLACK));
                header.add(title);
                add(header, BorderLayout.NORTH);
    
                header.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        AccordionListener listener = getAccordionListener();
                        if (listener != null) {
                            listener.accordionSelected(AccordionPanel.this);
                        }
                    }
                });
            }
    
            public AccordionPanel(String title) {
                this();
                setTitle(title);
            }
    
            public AccordionPanel(String title, Component content) {
                this(title);
                setContentPane(content);
            }
    
            public void setAccordionListener(AccordionListener accordionListener) {
                this.accordionListener = accordionListener;
            }
    
            public AccordionListener getAccordionListener() {
                return accordionListener;
            }
    
            public void setTitle(String text) {
                title.setText(text);
                revalidate();
            }
    
            public String getText() {
                return title.getText();
            }
    
            public void setContentPane(Component content) {
                if (this.content != null) {
                    remove(this.content);
                }
    
                this.content = content;
                if (this.content != null) {
                    add(this.content);
                }
                revalidate();
            }
    
            public Component getContent() {
                return content;
            }
    
            @Override
            public Dimension getMinimumSize() {
                return header.getPreferredSize();
            }
    
            @Override
            public Dimension getPreferredSize() {
                Dimension size = content != null ? content.getPreferredSize() : super.getPreferredSize();
                Dimension min = getMinimumSize();
                size.width = Math.max(min.width, size.width);
                size.height += min.height;
                return size;
            }
    
        }
    
        public class AccordionLayout implements LayoutManager {
    
            // This "could" be controled by constraints, but that would assume
            // that more then one component could be expanded at a time
            private Component expanded;
    
            public void setExpanded(Component expanded) {
                this.expanded = expanded;
            }
    
            public Component getExpanded() {
                return expanded;
            }
    
            @Override
            public void addLayoutComponent(String name, Component comp) {
            }
    
            @Override
            public void removeLayoutComponent(Component comp) {
            }
    
            @Override
            public Dimension preferredLayoutSize(Container parent) {
                Dimension size = minimumLayoutSize(parent);
                if (expanded != null) {
                    size.height -= expanded.getMinimumSize().height;
                    size.height += expanded.getPreferredSize().height;
                }
    
                return size;
            }
    
            @Override
            public Dimension minimumLayoutSize(Container parent) {
                int height = 0;
                int width = 0;
                for (Component comp : parent.getComponents()) {
                    width = Math.max(width, comp.getPreferredSize().width);
                    height += comp.getMinimumSize().height;
                }
                return new Dimension(width, height);
            }
    
            @Override
            public void layoutContainer(Container parent) {
    
                Insets insets = parent.getInsets();
                int availableHeight = parent.getHeight() - (insets.top + insets.bottom);
                int x = insets.left;
                int y = insets.top;
    
                int maxSize = 0;
                Dimension minSize = minimumLayoutSize(parent);
                if (expanded != null) {
                    minSize.height -= expanded.getMinimumSize().height;
                    // Try an honour the preferred size the expanded component...
                    maxSize = Math.max(expanded.getPreferredSize().height, availableHeight - minSize.height);
                }
    
                int width = parent.getWidth() - (insets.left + insets.right);
                for (Component comp : parent.getComponents()) {
                    if (expanded != comp) {
                        comp.setSize(width, comp.getMinimumSize().height);
                    } else {
                        comp.setSize(width, maxSize);
                    }
                    comp.setLocation(x, y);
                    y += comp.getHeight();
                }
    
            }
    
        }
    
    }
    

    Now, if you're really up for a challenge, you could use something a animated layout proxy and do something like...

    0 讨论(0)
  • 2020-12-04 03:56

    You can tell something is "crunched" when panel.getPreferredSize().height != panel.getHeight() and panel.getPreferredSize().width != panel.getWidth()

    0 讨论(0)
  • 2020-12-04 04:05

    The end goal is to remove older panels when a new panel is added and there isn't enough room for it

    I would guess that after you add a panel you compare the preferred height with the actual height. When the preferred height is greater you have a problem and you remove components as required.

    So then the next problem is to use a layout manager that doesn't change the heights of the panels. This can still be done with the GridBagLayout. You just need to override the getMinimumSize() method to return the getPreferredSize() Dimension.

    Each part of the accordion can open individually and they're of arbitrary size and get added on the fly

    You might want to consider using the Relative Layout. You can add components whose preferred size will be respected. So you will be able to check when the preferred height is greater than the actual height.

    Then you can also add components that will be sized based on the amount of space left in the panel. These would be your expanding panels.

    So in your example you example when you expand an item you could configure that component to take up the entire space available. If you expand two items then they would each get half the space available.

    Maybe something like this:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class ExpandingPanel extends JPanel
    {
        private JPanel expanding;
    
        public ExpandingPanel(String text, Color color)
        {
            setLayout( new BorderLayout() );
    
            JButton button = new JButton( text );
            add(button, BorderLayout.NORTH);
    
            expanding = new JPanel();
            expanding.setBackground( color );
            expanding.setVisible( false );
            add(expanding, BorderLayout.CENTER);
    
            button.addActionListener( new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent e)
                {
                    expanding.setVisible( !expanding.isVisible() );
    
                    Container parent = ExpandingPanel.this.getParent();
                    LayoutManager2 layout = (LayoutManager2)parent.getLayout();
    
                    if (expanding.isVisible())
                        layout.addLayoutComponent(ExpandingPanel.this, new Float(1));
                    else
                        layout.addLayoutComponent(ExpandingPanel.this, null);
    
                    revalidate();
                    repaint();
                }
            });
        }
    
        private static void createAndShowGUI()
        {
            RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
            rl.setFill( true );
    
            JPanel content = new JPanel( rl );
            content.add( new ExpandingPanel("Red", Color.RED) );
            content.add( new ExpandingPanel("Blue", Color.BLUE) );
            content.add( new ExpandingPanel("Green", Color.GREEN) );
    
            JFrame frame = new JFrame("Expanding Panel");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add( content);
            frame.setLocationByPlatform( true );
            frame.setSize(200, 300);
            frame.setVisible( true );
        }
    
        public static void main(String[] args)
        {
            EventQueue.invokeLater(new Runnable()
            {
                public void run()
                {
                    createAndShowGUI();
                }
            });
        }
    }
    
    0 讨论(0)
提交回复
热议问题