Reputation:
The layout of the components in the project I'm working on didn't look correct, I suspect there's a bug in Swing. Basically, what appears to be happening is that the weightx
and weighty
proportions aren't being adhered to when the cells being laid out have varying minimum sizes and/or preferred sizes. I created a sample program to demonstrate this, here is the source:
package com.ensoftcorp.product.simmerge.gui.swing.dialogs;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TestClass {
/**
* @param args
*/
public static void main(String[] args) {
try {
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new TestClass();
} catch (Exception e) {
e.printStackTrace();
}
}
static final GridBagConstraints GBC_CELL = new GridBagConstraints();
static final GridBagConstraints GBC_ROWEND = new GridBagConstraints();
static final GridBagConstraints GBC_FILLROW = new GridBagConstraints();
static {
GBC_CELL.anchor = GridBagConstraints.NORTHWEST;
GBC_CELL.weightx = 0.5;
GBC_CELL.fill = GridBagConstraints.BOTH;
GBC_ROWEND.gridwidth = GridBagConstraints.REMAINDER;
GBC_FILLROW.gridwidth = GridBagConstraints.REMAINDER;
GBC_FILLROW.weightx = 1.0;
GBC_FILLROW.fill = GridBagConstraints.BOTH;
}
public TestClass() {
JFrame frame = new JFrame();
JPanel pnlContent = new JPanel(new GridBagLayout());
pnlContent.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
/*
* Layout "ruler" panel
*/
JPanel pnlRuler = new JPanel(new GridBagLayout());
pnlRuler.add(createRulerCell(Color.BLACK, Color.WHITE),GBC_CELL);
pnlRuler.add(createRulerCell(Color.BLACK, Color.WHITE),GBC_CELL);
pnlRuler.add(Box.createHorizontalGlue(),GBC_ROWEND);
/*
* Layout "correct" panel
*/
JPanel pnlGoodLayout = new JPanel(new GridBagLayout());
pnlGoodLayout.add(new JButton("JButton1"),GBC_CELL);
pnlGoodLayout.add(new JButton("JButton2"),GBC_CELL);
pnlGoodLayout.add(Box.createHorizontalGlue(),GBC_ROWEND);
pnlGoodLayout.add(new JButton("JButton3"),GBC_CELL);
pnlGoodLayout.add(new JButton("JButton4"),GBC_CELL);
pnlGoodLayout.add(Box.createHorizontalGlue(),GBC_ROWEND);
/*
* Layout "incorrect" panel
*/
JPanel pnlBadLayout = new JPanel(new GridBagLayout());
pnlBadLayout.add(new JButton("JButton1"),GBC_CELL);
pnlBadLayout.add(new JButton("JButton2"),GBC_CELL);
pnlBadLayout.add(Box.createHorizontalGlue(),GBC_ROWEND);
pnlBadLayout.add(new JButton("JButton number 3 is wide"),GBC_CELL);
pnlBadLayout.add(new JButton("JButton4"),GBC_CELL);
pnlBadLayout.add(Box.createHorizontalGlue(),GBC_ROWEND);
/*
* Add panels to main panel
*/
pnlContent.add(pnlRuler,GBC_FILLROW);
pnlContent.add(Box.createVerticalStrut(8),GBC_FILLROW);
pnlContent.add(pnlGoodLayout,GBC_FILLROW);
pnlContent.add(Box.createVerticalStrut(8),GBC_FILLROW);
pnlContent.add(pnlBadLayout,GBC_FILLROW);
/*
* Configure frame
*/
frame.getContentPane().add(pnlContent);
frame.setTitle("GridBagLayout Weight Bug?");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,200);
frame.setVisible(true);
}
JComponent createRulerCell(Color border, Color background) {
JPanel glue = new JPanel();
glue.setBorder(BorderFactory.createLineBorder(border));
glue.setBackground(background);
return glue;
}
}
The sample program has three groups...the first is a "ruler" of sorts to show the 50% mark. The second and third groups are panels using a GridBagLayout where each cell has a weightx
of 0.5. The only difference between the groups is the button text length, yet the second group does not evenly proportion the columns even though it has enough space to do so.
My question then is this: has anyone encountered this problem before, and have a workaround they would recommend?
P.S. - I am using jdk1.6.0_11, if that makes a difference.
Upvotes: 3
Views: 3122
Reputation: 2545
I found a simple workaround. Explicitly set preferredSize.width
attribute of each JButton
to preferredSize.width=0
.
This is the way to do it:
private JButton createButton(String text) {
JButton button = new JButton(text);
button.setPreferredSize(new Dimension(0, button.getPreferredSize().height));
return button;
}
Here is your modified example (I marked the changes with // <==============
comment):
public class TestClass {
/**
* @param args
*/
public static void main(String[] args) {
try {
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new TestClass();
} catch (Exception e) {
e.printStackTrace();
}
}
static final GridBagConstraints GBC_CELL = new GridBagConstraints();
static final GridBagConstraints GBC_ROWEND = new GridBagConstraints();
static final GridBagConstraints GBC_FILLROW = new GridBagConstraints();
static {
GBC_CELL.anchor = GridBagConstraints.NORTHWEST;
GBC_CELL.weightx = 0.5;
GBC_CELL.fill = GridBagConstraints.BOTH;
GBC_ROWEND.gridwidth = GridBagConstraints.REMAINDER;
GBC_FILLROW.gridwidth = GridBagConstraints.REMAINDER;
GBC_FILLROW.weightx = 1.0;
GBC_FILLROW.fill = GridBagConstraints.BOTH;
}
public TestClass() {
JFrame frame = new JFrame();
JPanel pnlContent = new JPanel(new GridBagLayout());
pnlContent.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
/*
* Layout "ruler" panel
*/
JPanel pnlRuler = new JPanel(new GridBagLayout());
pnlRuler.add(createRulerCell(Color.BLACK, Color.WHITE),GBC_CELL);
pnlRuler.add(createRulerCell(Color.BLACK, Color.WHITE),GBC_CELL);
pnlRuler.add(Box.createHorizontalGlue(),GBC_ROWEND);
/*
* Layout "correct" panel
*/
JPanel pnlGoodLayout = new JPanel(new GridBagLayout());
pnlGoodLayout.add(createButton("JButton1"),GBC_CELL); // <==============
pnlGoodLayout.add(createButton("JButton2"),GBC_CELL); // <==============
pnlGoodLayout.add(Box.createHorizontalGlue(),GBC_ROWEND);
pnlGoodLayout.add(createButton("JButton3"),GBC_CELL); // <==============
pnlGoodLayout.add(createButton("JButton4"),GBC_CELL); // <==============
pnlGoodLayout.add(Box.createHorizontalGlue(),GBC_ROWEND);
/*
* Layout "incorrect" panel
*/
JPanel pnlBadLayout = new JPanel(new GridBagLayout());
pnlBadLayout.add(createButton("JButton1"),GBC_CELL); // <==============
pnlBadLayout.add(createButton("JButton2"),GBC_CELL); // <==============
pnlBadLayout.add(Box.createHorizontalGlue(),GBC_ROWEND);
pnlBadLayout.add(createButton("JButton number 3 is wide"),GBC_CELL); // <==============
pnlBadLayout.add(createButton("JButton4"),GBC_CELL); // <==============
pnlBadLayout.add(Box.createHorizontalGlue(),GBC_ROWEND);
/*
* Add panels to main panel
*/
pnlContent.add(pnlRuler,GBC_FILLROW);
pnlContent.add(Box.createVerticalStrut(8),GBC_FILLROW);
pnlContent.add(pnlGoodLayout,GBC_FILLROW);
pnlContent.add(Box.createVerticalStrut(8),GBC_FILLROW);
pnlContent.add(pnlBadLayout,GBC_FILLROW);
/*
* Configure frame
*/
frame.getContentPane().add(pnlContent);
frame.setTitle("GridBagLayout Weight Bug?");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,200);
frame.setVisible(true);
}
JComponent createRulerCell(Color border, Color background) {
JPanel glue = new JPanel();
glue.setBorder(BorderFactory.createLineBorder(border));
glue.setBackground(background);
return glue;
}
private JButton createButton(String text) {
JButton button = new JButton(text);
button.setPreferredSize(new Dimension(0, button.getPreferredSize().height));
return button;
}
}
And this is how the result looks. See? Now the space is distributed evenly:
Upvotes: 0
Reputation: 57237
This might help (source):
When more than one column has a non-zero weight, the excess space is distributed among the non-zero weight columns using the weight values. In particular, if the excess space is P pixels, and the column weights for column^i is weight^i, then column^i gets exactly (weight^i * P) / (sum-of-all-column-weights). For example, if column 1 has weight 1 and column 2 has weight 2 and the excess space is 90 pixels, column 1 will get 30 extra pixels and column 2 will get 60 extra pixels. Rows with a non-zero weight behave in similar fashion.
Upvotes: 2
Reputation: 69997
Use the built-in GroupLayout. You have more control than the GridBagLayout.
Upvotes: 2
Reputation: 39485
from the GridBagConstraints.weightx JavaDoc:
Specifies how to distribute extra horizontal space.
The grid bag layout manager calculates the weight of a column to be the maximum weightx of all the components in a column. If the resulting layout is smaller horizontally than the area it needs to fill, the extra space is distributed to each column in proportion to its weight.
with that in mind, i dont think this is a bug because the column with the wide button is calculated to need more space. once that and the normal button columns sizes are calced, the remaining space is distributed.
Workaround:
to work around this, you can set GBC_CELL.gridx
and GBC_CELL.gridy
before adding each of your buttons, and before the button that shares the row with the large button, you could set
GBC_CELL.ipadx=100;
and then set it to 0
before adding the wide button (#3). this should align your buttons in more of a grid.
Upvotes: 5