试图获得一个swing菜单来使用GridBagLayout

我正在尝试构建一个带有两个垂直列的应用程序,锚定在左侧,右侧有一个大框。 我可以让第一个菜单坚持到右边,但由于某种原因,第二个菜单不会出现在第一个菜单旁边。 我已经阅读了有关额外空间被推向最后一列和最后一行(右侧)的内容。 我该怎么处理呢?

PS我正在使用网格袋布局。

这是我得到的:

主UserView类:package 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(); } } 

我想要得到的: 在此处输入图像描述

提前致谢!

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

您还应该看看如何使用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 ,因为它们通过控制器绑定在一起。

您可以进一步扩展控制器以提供其他function,以触发创建子视图的操作,或直接通过控制器提供手段以打开您提供的子视图。

我想要certificate这一点的原因是因为,当你添加新的子菜单时,它会影响剩余的内容区域,如果你试图在一个布局/容器中维护它,你会不断努力保持它们在一起并且很好地协同工作。

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

就像一个想法

可运行的例子……

放在一起

 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:这是一个不完整的解决方案,旨在在处理这类问题时促进“不同的思维方式”

我已经尝试了两个垂直Box es(布局),用于JPanel (按钮面板)中的按钮, GridLayout有两列和一行。 此按钮面板位于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; } } 

示例的输出:

在此处输入图像描述

“constraints.weightx”是你的朋友。

来自Javadoc的“ double java.awt.GridBagConstraints.weightx ”:

网格包布局管理器计算列的权重,使其成为列中所有组件的最大权重。 如果生成的布局在水平方向上比需要填充的区域小,则额外的空间将按其重量的比例分配到每列。 权重为零的列不会占用额外空间。

这意味着如果指定值“0”,则下一个组件将紧邻前一个组件分发。

在测试代​​码之后,我能够通过在initialize()函数上操作constraints.weightx来实现您的目标。 但是有一个技巧,如果所有的MenuPanel都可见,我只能应用它!

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

 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++; 

结果: 在此处输入图像描述

如果您需要隐藏组件(仅在按下按钮时显示),您需要更新主框架布局的约束,以便它们正确显示。

我希望这可以帮助你。