尝试获取一个摆动菜单以与GridBagLayout一起使用

时间:2018-08-30 10:32:01

标签: java swing layout-manager gridbaglayout

我正在尝试构建一个在左侧锚定两个垂直列的应用程序, 还有一个大盒子在右边我可以将第一个菜单贴在右边, 但由于某种原因,第二个菜单不会出现在第一个菜单的旁边。我已经读到一些有关将多余的空间推向最后一列和最后一行(右侧)的内容。我应该如何处理?

P.S。我正在使用网格袋布局。

这就是我所拥有的:

主要的UserView类:     包gui;

import actions.DepositAddButtonAction;
import actions.DepositButtonAction;

import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;

public class UserView {
    public JFrame frame;
    private JPanel menuPanel;
    private JPanel secondMenuPanel;
    private JPanel contentPanel;
    private JButton depositButton;
    private JButton creditButton;
    private JButton exchangeButton;
    private JButton simulationButton;
    private JButton informationButton;
    private JLabel menuLabel;
    private GridBagLayout gridBagLayout;
    private GridBagConstraints constraints;
    private Border border;

    public UserView() {
        frame = new JFrame("E-Banking");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        menuPanel = new JPanel();
        secondMenuPanel = new JPanel();
        contentPanel = new JPanel();
        depositButton = new JButton("Deposit: ", new ImageIcon(UserView.class.getResource("/money_box_icon.png")));
        creditButton = new JButton("Credit: ", new ImageIcon(UserView.class.getResource("/credit_icon.png")));
        exchangeButton = new JButton("Exchange: ", new ImageIcon(UserView.class.getResource("/exchange_icon.png")));
        simulationButton = new JButton("Simulation: ", new ImageIcon(UserView.class.getResource("/simulation_icon.png")));
        informationButton = new JButton("Information: ", new ImageIcon(UserView.class.getResource("/info_icon.png")));
        menuLabel = new JLabel(new ImageIcon(UserView.class.getResource("/bank_icon.png")), SwingConstants.LEFT);
        gridBagLayout = new GridBagLayout();
        constraints = new GridBagConstraints();
        border = BorderFactory.createLineBorder(new Color(102, 102, 153), 1, true);
        frame.setSize(800, 600);
        applyButtonStyles();
        initialize();
    }

    private void applyButtonStyles() {
        depositButton.setHorizontalTextPosition(SwingConstants.RIGHT);
        creditButton.setHorizontalTextPosition(SwingConstants.RIGHT);
        exchangeButton.setHorizontalTextPosition(SwingConstants.RIGHT);
        simulationButton.setHorizontalTextPosition(SwingConstants.RIGHT);
        informationButton.setHorizontalTextPosition(SwingConstants.RIGHT);
        menuLabel.setHorizontalAlignment(SwingConstants.RIGHT);

        menuPanel.setBorder(border);
        secondMenuPanel.setBorder(border);
        secondMenuPanel.setVisible(false);
        contentPanel.setBorder(border);
        contentPanel.setVisible(false);

    }

    private void initialize() {
        menuLabel.setText("E-Banking");

        constraints.gridx = 0;
        constraints.gridy = 0;
        constraints.anchor = GridBagConstraints.FIRST_LINE_START;
        constraints.fill = GridBagConstraints.BOTH;
        constraints.insets = new Insets(4, 4, 4, 4);
        menuPanel.setLayout(gridBagLayout);

        menuPanel.add(menuLabel);
        constraints.gridy++;
        menuPanel.add(depositButton, constraints);
        constraints.gridy++;
        menuPanel.add(creditButton, constraints);
        constraints.gridy++;
        menuPanel.add(exchangeButton, constraints);
        constraints.gridy++;
        menuPanel.add(simulationButton, constraints);
        constraints.gridy++;
        menuPanel.add(informationButton, constraints);

        constraints.gridx = 1;
        constraints.gridy = 0;

        frame.getContentPane().setLayout(gridBagLayout);
        constraints.gridx = 0;
        constraints.gridy = 0;

        constraints.weightx = 0.4;
        constraints.weighty = 0.4;
        constraints.fill = GridBagConstraints.NONE;
        frame.getContentPane().add(menuPanel, constraints);
        constraints.gridx++;

        frame.getContentPane().add(secondMenuPanel, constraints);
        constraints.gridx++;

        frame.getContentPane().add(contentPanel, constraints);
        constraints.gridx++;

        DepositAddButtonAction depositAddButtonAction = new DepositAddButtonAction(contentPanel);
        DepositButtonAction depositButtonAction = new DepositButtonAction(secondMenuPanel, contentPanel, depositAddButtonAction, null, null);
        depositButton.addActionListener(depositButtonAction);


        }

}

另一个代表第一个按钮行为的类:

package actions;

import gui.UserView;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class DepositButtonAction implements ActionListener {
    private JPanel secondMenuPanel;
    private JPanel contentPanel;
    private JButton addButton;
    private JButton queryButton;
    private JLabel operationLabel;
    private GridBagLayout gridBagLayout;
    private GridBagConstraints constraints;

    public DepositButtonAction(JPanel secondMenuPanel, JPanel contentPanel, ActionListener otherDepositAddButtonAction,
                               ActionListener otherDepositRemoveButtonAction, ActionListener otherDepositQueryButtonAction) {

        this.secondMenuPanel = secondMenuPanel;
        secondMenuPanel.setVisible(false);
        this.contentPanel = contentPanel;
        addButton = new JButton("Request", new ImageIcon(UserView.class.getResource("/add_icon.png")));
        queryButton = new JButton("Query", new ImageIcon(UserView.class.getResource("/info_icon.png")));
        operationLabel = new JLabel(new ImageIcon(UserView.class.getResource("/options_icon.png")));
        operationLabel.setText("Options ");
        gridBagLayout = new GridBagLayout();
        constraints = new GridBagConstraints();
        applyStyles();
        addButton.addActionListener(otherDepositAddButtonAction);
//        removeButton.addActionListener(otherDepositRemoveButtonAction);
//        queryButton.addActionListener(otherDepositQueryButtonAction);
    }

    private void applyStyles() {
        secondMenuPanel.setLayout(gridBagLayout);
        addButton.setHorizontalTextPosition(SwingConstants.RIGHT);
        queryButton.setHorizontalTextPosition(SwingConstants.RIGHT);
    }

    private void initialize() {
        constraints.gridx = 0;
        constraints.gridy = 0;
        constraints.anchor = GridBagConstraints.FIRST_LINE_START;
        constraints.fill = GridBagConstraints.BOTH;


        constraints.insets = new Insets(4, 4, 4, 4);
        secondMenuPanel.add(operationLabel, constraints);
        constraints.gridy++;

        secondMenuPanel.add(addButton, constraints);
        constraints.gridy++;

        secondMenuPanel.add(queryButton, constraints);

    }


    @Override
    public void actionPerformed(ActionEvent arg0) {
        secondMenuPanel.setVisible(true);
        contentPanel.setVisible(false);
        secondMenuPanel.removeAll();
        contentPanel.removeAll();
        contentPanel.revalidate();
        contentPanel.repaint();
        initialize();
        secondMenuPanel.revalidate();
        secondMenuPanel.repaint();
    }

}

我想要得到的是: enter image description here

谢谢!

3 个答案:

答案 0 :(得分:3)

我用两个Box(两列一行)的JPanel(按钮面板)中的按钮使用了两个垂直的GridLayout es(布局)进行了尝试。此按钮面板位于JFrame的左侧(BorderLayout.WEST)。

示例代码:

import java.awt.*;
import javax.swing.*;

public class TestingLayout {

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

    private static void gui() {
        JFrame frame = new JFrame();
        frame.setTitle("JButtons Layout");

        JPanel pane = new JPanel();
        pane.setLayout(new GridLayout(1, 2));
        pane.add(getLeftButtons());
        pane.add(getRightButtons());

        frame.add(pane, BorderLayout.WEST);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setSize(800, 500);        
        frame.setVisible(true);
    }

    private static Box getLeftButtons() {
        Box box = Box.createVerticalBox();
        box.add(new JButton("b1"));
        box.add(new JButton("b2"));
        box.add(new JButton("b3"));
        box.add(new JButton("b4"));
        box.add(new JButton("b5"));
        return box;
    }

    private static Box getRightButtons() {
        Box box = Box.createVerticalBox();
        box.add(new JButton("b11"));
        box.add(new JButton("b12"));
        box.add(new JButton("b13"));
        return box;
    }
}


示例的输出:

enter image description here

答案 1 :(得分:2)

请记住,您不仅限于使用单个布局管理器。您可以通过使用多个容器来组合布局管理器,从而使您可以专注于每个容器的个性化需求,并进一步隔离功能并降低重叠的布局要求的复杂性。

您还应该查看How to Use CardLayout,这将为您提供在不同视图之间切换的方式。

为此,我采取了一种略有不同的方式,从“菜单容器”的概念开始,其中可以包含“子菜单”

public class MenuPane extends JPanel {

    public MenuPane() {
        setLayout(new GridBagLayout());
    }

    public void addSubMenuPane(SubMenuPane pane) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridy = 0;
        gbc.anchor = GridBagConstraints.NORTH;
        gbc.weighty = 1;
        add(pane, gbc);

        revalidate();
        repaint();
    }

    public void removeSubMenu(SubMenuPane pane) {
        remove(pane);
        revalidate();
        repaint();
    }

    public void popLastMenu() {
        if (getComponentCount() == 1) {
            return;
        }
        remove(getComponent(getComponentCount() - 1));
        revalidate();
        repaint();
    }

}

public class SubMenuPane extends JPanel {

    public SubMenuPane(String name) {
        setLayout(new GridLayout(0, 1));
        setBorder(new LineBorder(Color.DARK_GRAY));
        add(new JLabel(name, JLabel.CENTER));
    }

    public SubMenuPane addAction(MenuAction action) {
        JButton btn = new JButton(action);
        add(btn);
        return this;
    }

}

我想做的事情是分离API的各个部分,并减少API的任何一部分对其余部分的了解。

基本上,这体现在SubMenuPane中,它只是一些按钮的容器,这些按钮是通过MenuAction类进行配置和管理的,除此之外,它什么也不做。

public interface MenuAction extends Action {

    public MenuController getController();
}

public abstract class AbstractMenuAction extends AbstractAction implements MenuAction {

    private MenuController controller;

    public AbstractMenuAction(MenuController controller, String name) {
        this.controller = controller;
        putValue(NAME, name);
    }

    @Override
    public MenuController getController() {
        return controller;
    }

}

MenuAction基于Actions API,它提供了一个独立且可配置的工作单元

出于很好的考虑,我把一个控制器放在菜单窗格和菜单操作之间。

public interface MenuController {
    public void addSubMenu(SubMenuPane subMenuPane);
    public void popLastMenu();
}

public class DefaultMenuController implements MenuController {

    private MenuPane menuPane;

    public DefaultMenuController(MenuPane menuPane) {
        this.menuPane = menuPane;
    }

    @Override
    public void addSubMenu(SubMenuPane subMenuPane) {
        menuPane.addSubMenuPane(subMenuPane);
    }

    @Override
    public void popLastMenu() {
        menuPane.popLastMenu();
    }

}

这里的意图是,我不希望这些操作能够不利地修改菜单窗格,相反,我将它们限制为仅两个操作。

好的,但这对您有什么帮助?

好吧,让我们建立一个菜单并找出...

MenuPane menuPane = new MenuPane();
DefaultMenuController controller = new DefaultMenuController(menuPane);

SubMenuPane ebanking = new SubMenuPane("E-Banking");
ebanking.addAction(new AbstractMenuAction(controller, "Deposit") {
    @Override
    public void actionPerformed(ActionEvent e) {
        getController().popLastMenu();
        SubMenuPane deposit = new SubMenuPane("Options").addAction(new AbstractMenuAction(getController(), "Request") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        }).addAction(new AbstractMenuAction(getController(), "Query") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        });
        getController().addSubMenu(deposit);
    }
}).addAction(new AbstractMenuAction(controller, "Credit") {
    @Override
    public void actionPerformed(ActionEvent e) {
        getController().popLastMenu();
        SubMenuPane deposit = new SubMenuPane("Credit-Options").addAction(new AbstractMenuAction(getController(), "Request") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        }).addAction(new AbstractMenuAction(getController(), "Query") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        });
        getController().addSubMenu(deposit);
    }
}).addAction(new AbstractMenuAction(controller, "Exchange") {
    @Override
    public void actionPerformed(ActionEvent e) {
        getController().popLastMenu();
        SubMenuPane deposit = new SubMenuPane("Exchange-Options").addAction(new AbstractMenuAction(getController(), "Request") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        }).addAction(new AbstractMenuAction(getController(), "Query") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        });
        getController().addSubMenu(deposit);
    }
}).addAction(new AbstractMenuAction(controller, "Simulation") {
    @Override
    public void actionPerformed(ActionEvent e) {
        getController().popLastMenu();
        SubMenuPane deposit = new SubMenuPane("Simulation-Options").addAction(new AbstractMenuAction(getController(), "Request") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        }).addAction(new AbstractMenuAction(getController(), "Query") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        });
        getController().addSubMenu(deposit);
    }
}).addAction(new AbstractMenuAction(controller, "Information") {
    @Override
    public void actionPerformed(ActionEvent e) {
        getController().popLastMenu();
        SubMenuPane deposit = new SubMenuPane("Information-Options").addAction(new AbstractMenuAction(getController(), "Request") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        }).addAction(new AbstractMenuAction(getController(), "Query") {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Use Card layout to show next avaliable options
            }
        });
        getController().addSubMenu(deposit);
    }
});
controller.addSubMenu(ebanking);

好的,所以有很多“什么……代码”。为了简洁起见,我使用了许多匿名类,实际上,我可能为每个菜单操作都设置了子类,但这提供了基础工作。

关键是,每个子菜单都可以通过控制器绑定在一起而独立于MenuPane进行简单,轻松地制作。

您可以进一步扩展控制器以提供其他功能,以触发创建子视图的操作,或者直接通过控制器提供一种方法来打开您提供的子视图。

我之所以要证明这一点是因为,当您添加新的子菜单时,它将对剩余内容区域产生影响,如果您尝试将其维护在单个布局/容器中,则您将不断挣扎尝试将所有内容保持在一起并一起良好地工作。

相反,此解决方案包含其自己的容器的子菜单,该子菜单仅管理菜单,然后将其添加到另一个容器,可能使用其他布局管理器,然后分别管理外部问题。

只是一个想法

可运行的示例...

Putting to together

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
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();
                }

                MenuPane menuPane = new MenuPane();
                DefaultMenuController controller = new DefaultMenuController(menuPane);

                SubMenuPane ebanking = new SubMenuPane("E-Banking");
                ebanking.addAction(new AbstractMenuAction(controller, "Deposit") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        getController().popLastMenu();
                        SubMenuPane deposit = new SubMenuPane("Options").addAction(new AbstractMenuAction(getController(), "Request") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        }).addAction(new AbstractMenuAction(getController(), "Query") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        });
                        getController().addSubMenu(deposit);
                    }
                }).addAction(new AbstractMenuAction(controller, "Credit") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        getController().popLastMenu();
                        SubMenuPane deposit = new SubMenuPane("Credit-Options").addAction(new AbstractMenuAction(getController(), "Request") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        }).addAction(new AbstractMenuAction(getController(), "Query") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        });
                        getController().addSubMenu(deposit);
                    }
                }).addAction(new AbstractMenuAction(controller, "Exchange") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        getController().popLastMenu();
                        SubMenuPane deposit = new SubMenuPane("Exchange-Options").addAction(new AbstractMenuAction(getController(), "Request") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        }).addAction(new AbstractMenuAction(getController(), "Query") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        });
                        getController().addSubMenu(deposit);
                    }
                }).addAction(new AbstractMenuAction(controller, "Simulation") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        getController().popLastMenu();
                        SubMenuPane deposit = new SubMenuPane("Simulation-Options").addAction(new AbstractMenuAction(getController(), "Request") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        }).addAction(new AbstractMenuAction(getController(), "Query") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        });
                        getController().addSubMenu(deposit);
                    }
                }).addAction(new AbstractMenuAction(controller, "Information") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        getController().popLastMenu();
                        SubMenuPane deposit = new SubMenuPane("Information-Options").addAction(new AbstractMenuAction(getController(), "Request") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        }).addAction(new AbstractMenuAction(getController(), "Query") {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                // Use Card layout to show next avaliable options
                            }
                        });
                        getController().addSubMenu(deposit);
                    }
                });
                controller.addSubMenu(ebanking);

                JPanel someContent = new JPanel() {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(200, 200);
                    }
                };
                someContent.setBackground(Color.RED);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(menuPane, BorderLayout.WEST);
                frame.add(someContent);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface MenuController {

        public void addSubMenu(SubMenuPane subMenuPane);

        public void popLastMenu();
    }

    public class DefaultMenuController implements MenuController {

        private MenuPane menuPane;

        public DefaultMenuController(MenuPane menuPane) {
            this.menuPane = menuPane;
        }

        @Override
        public void addSubMenu(SubMenuPane subMenuPane) {
            menuPane.addSubMenuPane(subMenuPane);
        }

        @Override
        public void popLastMenu() {
            menuPane.popLastMenu();
        }

    }

    public interface MenuAction extends Action {

        public MenuController getController();
    }

    public abstract class AbstractMenuAction extends AbstractAction implements MenuAction {

        private MenuController controller;

        public AbstractMenuAction(MenuController controller, String name) {
            this.controller = controller;
            putValue(NAME, name);
        }

        @Override
        public MenuController getController() {
            return controller;
        }

    }

    public class MenuPane extends JPanel {

        public MenuPane() {
            setLayout(new GridBagLayout());
        }

        public void addSubMenuPane(SubMenuPane pane) {
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.NORTH;
            gbc.weighty = 1;
            add(pane, gbc);

            revalidate();
            repaint();
        }

        public void removeSubMenu(SubMenuPane pane) {
            remove(pane);
            revalidate();
            repaint();
        }

        public void popLastMenu() {
            if (getComponentCount() == 1) {
                return;
            }
            remove(getComponent(getComponentCount() - 1));
            revalidate();
            repaint();
        }

    }

    public class SubMenuPane extends JPanel {

        public SubMenuPane(String name) {
            setLayout(new GridLayout(0, 1));
            setBorder(new LineBorder(Color.DARK_GRAY));
            add(new JLabel(name, JLabel.CENTER));
        }

        public SubMenuPane addAction(MenuAction action) {
            JButton btn = new JButton(action);
            add(btn);
            return this;
        }

        public void pop() {
            Container parent = getParent();
            if (parent != null) {
                parent.remove(this);
                parent.revalidate();
                parent.repaint();
            }
        }

    }

}

nb:这是一个不完整的解决方案,旨在解决此类问题时会提倡“不同的思维方式”

答案 2 :(得分:1)

“ constraints.weightx”是你的朋友。

从Javadoc中获取“ double java.awt.GridBagConstraints.weightx ”:

  

网格袋布局管理器将一列的重量计算为一列中所有组件的最大权重x。如果生成的布局在水平方向上小于需要填充的区域,则多余的空间将按其重量成比例地分配给每列。权重为零的列不会收到多余的空间。

这意味着,如果您指定值“ 0”,则下一个组件将被分配在上一个组件的旁边。

在测试完代码之后,我可以通过操作 initialize() 函数上的constraints.weightx来达到您的期望。不过有一个窍门,只有在所有 MenuPanels 都可见的情况下,我才能应用此技巧!

我所做的就是这样,所有菜单面板都可见:

constraints.weightx = secondMenuPanel.isVisible() ? 0.0 : 0.4;
constraints.weighty = 0.4;
constraints.fill = GridBagConstraints.NONE;
frame.getContentPane().add(menuPanel, constraints);
constraints.gridx++;

constraints.weightx = 0.4;
frame.getContentPane().add(secondMenuPanel, constraints);
constraints.gridx++;

结果: enter image description here

如果需要在隐藏组件的情况下执行此操作(仅在按下按钮时显示),则需要更新主框架布局的约束,以使其正确显示。

希望对您有帮助。