Java 尝试使用GridBagLayout获得swing菜单

Java 尝试使用GridBagLayout获得swing菜单,java,swing,layout-manager,gridbaglayout,Java,Swing,Layout Manager,Gridbaglayout,我正在尝试构建一个包含两个垂直列的应用程序,锚定在左侧, 右边有一个大盒子。我可以把第一份菜单放在右边, 但由于某些原因,第二个菜单不会出现在第一个菜单旁边。我已经读到一些关于额外的空间被推向最后一列和最后一行(右边)。我该怎么处理呢 另外,我正在使用网格袋布局 以下是我得到的: 主用户视图类: 软件包图形用户界面 import actions.DepositAddButtonAction; import actions.DepositButtonAction; import javax.sw

我正在尝试构建一个包含两个垂直列的应用程序,锚定在左侧, 右边有一个大盒子。我可以把第一份菜单放在右边, 但由于某些原因,第二个菜单不会出现在第一个菜单旁边。我已经读到一些关于额外的空间被推向最后一列和最后一行(右边)。我该怎么处理呢

另外,我正在使用网格袋布局

以下是我得到的:

主用户视图类: 软件包图形用户界面

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

}
我想要得到的是:

提前谢谢

“constraints.weightx”是你的朋友

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

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

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

在测试代码之后,我能够通过操纵initialize()函数上的constraints.weightx实现您想要的功能。不过有一个技巧,我只能在所有的菜单面板都可见的情况下应用它

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

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++;
结果:

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

我希望这对您有所帮助。

约束.weightx是您的朋友

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

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

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

在测试代码之后,我能够通过操纵initialize()函数上的constraints.weightx实现您想要的功能。不过有一个技巧,我只能在所有的菜单面板都可见的情况下应用它

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

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++;
结果:

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


我希望这能对您有所帮助。

我尝试了两个垂直的
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;
    }
}

示例的输出:


我已经尝试过使用两个垂直的
es(布局)来设置
JPanel
(按钮面板)中的按钮,该按钮具有两列一行的
网格布局。此按钮面板位于
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;
    }
}

示例的输出:


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

您还应该看看,这将为您提供在不同视图之间翻转的方法

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

我想做的事情是将API的各个部分解耦,并减少API的任何一部分对其余部分的知识量

基本上,这体现在
子菜单窗格
中,它只是一些按钮的容器,这些按钮通过
菜单操作
类配置和管理,除此之外,它什么也不做

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
基于,它提供了一个自包含且可配置的工作单元

为了更好的衡量,我在菜单窗格和菜单操作之间加入了一个控制器

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);
好吧,这是很多“什么…代码”。为了简洁起见,我使用了很多匿名类,实际上,我可能为每个菜单操作设置了子类,但这提供了基本的基础工作

关键是,每个子菜单都可以简单方便地制作,独立于
菜单页
,因为它们通过控制器绑定在一起

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

我想展示这个想法的原因是,当您添加新的子菜单时,它将影响剩余的内容区域,如果您试图在单个布局/容器中维护它,那么您将不断地努力将所有内容保持在一起并很好地协同工作<