如何用GridBagLayout实现BoxLayout式的组件垂直居中?
问题描述
我想通过GridBagLayout实现类似BoxLayout中Box.createVerticalGlue()的组件垂直居中效果。最初采用垂直Box布局:
Box box = Box.createVerticalBox(); box.add(Box.createVerticalGlue()); box.add(add); box.add(remove); box.add(edit); box.add(Box.createVerticalGlue()); JPanel internalPanel = new JPanel(new BorderLayout()); internalPanel.add(keywordsScrollPane, BorderLayout.CENTER); internalPanel.add(box, BorderLayout.EAST);
但按钮尺寸不一致导致布局混乱。改用GridBagLayout后,通过GridBagConstraints.fill解决了按钮宽度统一的问题,却无法实现组件垂直居中:调整网格行、GridBagConstraints.anchor和GridBagConstraints.weightY后,按钮间距过大。我希望按钮能像Box布局那样紧凑分组并垂直居中,如何用GridBagLayout实现?我使用了自定义的ConstraintsBuilder类,相关代码如下:
public class KeywordsDialog extends JDialog implements ActionListener, ListSelectionListener { private JList<String> keywords; private JScrollPane keywordsScrollPane; private JButton add; private JButton remove; private JButton edit; private Set<String> keywordsList; public KeywordsDialog(Window parent, Collection<String> keywordsList) { super(parent); this.keywordsList = keywordsList == null ? new HashSet<String>() : new HashSet<String>(keywordsList); if (keywordsList != null && !keywordsList.isEmpty()) { this.keywords = new JList<String>(toListModel(keywordsList)); } else { this.keywords = new JList<String>(new DefaultListModel<String>()); } this.keywordsScrollPane = new JScrollPane(keywords); this.add = new JButton("Add"); this.remove = new JButton("Remove"); this.edit = new JButton("Edit"); this.edit.setEnabled(false); this.add.setEnabled(false); ConstraintsBuilder builder = LayoutUtils.gridBagConstraintsBuilder(); JPanel internalPanel = new JPanel(new GridBagLayout()); internalPanel.add(this.keywordsScrollPane, builder.gridX(0).gridY(0).gridHeight(3).margins(0, 0, 0, 5) .fill(GridBagConstraints.BOTH).weightX(1D).weightY(1D).build()); internalPanel.add(this.add, builder.reset().gridX(1).gridY(0).fill(GridBagConstraints.HORIZONTAL).weightX(1D).weightY(1D).build()); internalPanel.add(this.remove, builder.reset().gridX(1).gridY(1).fill(GridBagConstraints.HORIZONTAL).weightX(1D).weightY(1D).build()); internalPanel.add(this.edit, builder.reset().gridX(1).gridY(2).fill(GridBagConstraints.HORIZONTAL).weightX(1D).weightY(1D).build()); this.keywords.setBorder(BorderFactory.createTitledBorder("Keywords")); internalPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); this.setLayout(new BorderLayout()); this.add(internalPanel, BorderLayout.CENTER); Dimension screen = GuiHelper.getScreenSize(parent); this.setSize((int) (screen.getWidth() / 4), (int) (screen.getHeight() / 3)); this.setLocationRelativeTo(parent); this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); } // ... }
解决方案
要实现按钮紧凑分组且垂直居中,核心是调整GridBagConstraints的weightY和anchor属性,同时给按钮组上下添加占位的“弹性行”,具体修改如下:
ConstraintsBuilder builder = LayoutUtils.gridBagConstraintsBuilder(); JPanel internalPanel = new JPanel(new GridBagLayout()); // 左侧列表,占据所有垂直空间 internalPanel.add(this.keywordsScrollPane, builder.gridX(0).gridY(1).gridHeight(3).margins(0, 0, 0, 5) .fill(GridBagConstraints.BOTH).weightX(1D).weightY(1D).build()); // 顶部占位行,占据上方剩余垂直空间 internalPanel.add(Box.createGlue(), builder.reset().gridX(1).gridY(0).fill(GridBagConstraints.VERTICAL) .weightX(1D).weightY(1D).build()); // 按钮设置:weightY设为0,保持紧凑排列 internalPanel.add(this.add, builder.reset().gridX(1).gridY(1).fill(GridBagConstraints.HORIZONTAL).weightX(1D).weightY(0D).anchor(GridBagConstraints.CENTER).build()); internalPanel.add(this.remove, builder.reset().gridX(1).gridY(2).fill(GridBagConstraints.HORIZONTAL).weightX(1D).weightY(0D).anchor(GridBagConstraints.CENTER).build()); internalPanel.add(this.edit, builder.reset().gridX(1).gridY(3).fill(GridBagConstraints.HORIZONTAL).weightX(1D).weightY(0D).anchor(GridBagConstraints.CENTER).build()); // 底部占位行,占据下方剩余垂直空间 internalPanel.add(Box.createGlue(), builder.reset().gridX(1).gridY(4).fill(GridBagConstraints.VERTICAL) .weightX(1D).weightY(1D).build());
关键调整说明
- 占位行:用
Box.createGlue()作为占位组件,设置fill(VERTICAL)和weightY(1D),让它们自动占据上下的剩余垂直空间,将按钮组挤压到容器垂直中间位置,逻辑和BoxLayout的VerticalGlue完全一致。 - 按钮的
weightY(0D):告诉布局管理器不要给按钮分配额外垂直空间,按钮会保持自身的最佳尺寸,彼此紧凑排列,不会被拉伸导致间距过大。 anchor(CENTER):确保按钮在单元格内垂直居中,避免因单元格高度变化导致按钮偏移。
内容的提问来源于stack exchange,提问作者Cardinal System
相关产品推荐
相关产品推荐

