Java 如何填充布局管理器的剩余空间?
我最近一直在开发一个Swing应用程序。我遇到了布局管理器的问题。我似乎不知道如何使布局中的组件一直增长到其父容器的边缘。让我解释一下 假设我有8个按钮都在一行。根据窗口大小,将确定它们是否占用所有空间。GBL我找到了中心,所以左边和右边都有空间。BoxLayout通常在右侧留出空间。这可能是由于它们的锚或对齐 我认为问题在于,当所有组件都具有相同的设置时,布局会试图为每个组件提供相同的空间。因此,很少的额外空间不能被平均分配到他们忽略的每个组件 我想知道是否有解决这个问题的办法。就像空间是如此之小,我希望有一种方法可以让最后一个组件吃掉它,或者在组件之间尽可能地分配它 下面是显示问题的示例代码。注意:调整面板大小时,会获得额外的空间Java 如何填充布局管理器的剩余空间?,java,swing,layout-manager,Java,Swing,Layout Manager,我最近一直在开发一个Swing应用程序。我遇到了布局管理器的问题。我似乎不知道如何使布局中的组件一直增长到其父容器的边缘。让我解释一下 假设我有8个按钮都在一行。根据窗口大小,将确定它们是否占用所有空间。GBL我找到了中心,所以左边和右边都有空间。BoxLayout通常在右侧留出空间。这可能是由于它们的锚或对齐 我认为问题在于,当所有组件都具有相同的设置时,布局会试图为每个组件提供相同的空间。因此,很少的额外空间不能被平均分配到他们忽略的每个组件 我想知道是否有解决这个问题的办法。就像空间是如此
public class LeftoverExample {
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
LeftoverExample.createGUI();
}
});
}
public static void createGUI(){
JFrame jF = new JFrame();
jF.setSize(new Dimension(1333,500));
jF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create ContentPane
JPanel contentPane = new JPanel();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS));
GridBagLayout gBL = new GridBagLayout();
gBL.columnWidths = new int[]{0};
gBL.rowHeights = new int[]{50, 50, 50 , 50};
contentPane.setLayout(gBL);
//Initial Constraints
GridBagConstraints gBC = new GridBagConstraints();
gBC.fill = GridBagConstraints.BOTH;
gBC.gridx = 0;
gBC.gridy = 0;
gBC.weightx = 1;
gBC.weighty = 0;
gBC.insets = new Insets(10, 0, 10, 0);
//Add Examples to ContentPane
contentPane.add(LeftoverExample.createGBL(false), gBC);
gBC.gridy++;
contentPane.add(LeftoverExample.createGBL(true), gBC);
gBC.gridy++;
contentPane.add(LeftoverExample.createBoxLayout(false), gBC);
gBC.gridy++;
contentPane.add(LeftoverExample.createBoxLayout(true), gBC);
//Final
jF.setContentPane(contentPane);
jF.setVisible(true);
}
private static JComponent createGBL(boolean addButtons){
//GBL Example
JLabel gBLJLabel = new JLabel("GridBagLayout");
gBLJLabel.setVerticalAlignment(SwingConstants.CENTER);
gBLJLabel.setLayout(new BoxLayout(gBLJLabel, BoxLayout.X_AXIS));
gBLJLabel.setBackground(Color.CYAN);
gBLJLabel.setOpaque(true);
gBLJLabel.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
GridBagLayout gBL = new GridBagLayout();
gBL.columnWidths = new int[]{0};
gBL.rowHeights = new int[]{50};
gBLJLabel.setLayout(gBL);
//Initial Constraints
GridBagConstraints gBC = new GridBagConstraints();
gBC.fill = GridBagConstraints.BOTH;
gBC.gridx = 0;
gBC.gridy = 0;
gBC.weightx = 1;
gBC.weighty = 0;
gBC.insets = new Insets(0, 0, 0, 0);
//Add to GBL Panel
if(addButtons){
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
}
return gBLJLabel;
}
private static JComponent createBoxLayout(boolean addButtons){
//BoxLayout Example
JLabel boxLayoutJL = new JLabel("BOX_LAYOUT");
boxLayoutJL.setVerticalAlignment(SwingConstants.CENTER);
boxLayoutJL.setLayout(new BoxLayout(boxLayoutJL, BoxLayout.X_AXIS));
boxLayoutJL.setBackground(Color.GREEN);
boxLayoutJL.setOpaque(true);
boxLayoutJL.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
//Add to BoxLayout Panel
if(addButtons){
LeftoverExample.addButtons(boxLayoutJL);
LeftoverExample.addButtons(boxLayoutJL);
LeftoverExample.addButtons(boxLayoutJL);
}
return boxLayoutJL;
}
private static JButton createButton(Color c){
JButton jB = new JButton();
jB.setBackground(c);
jB.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
return jB;
}
private static void addButtons(JComponent jC, GridBagConstraints gBC){
//Create Buttons
Color[] colorA = {Color.RED, Color.BLUE, Color.BLACK, Color.GREEN};
for(Color c : colorA){
jC.add(LeftoverExample.createButton(c), gBC);
gBC.gridx++;
}
}
private static void addButtons(JComponent jC){
//Create Buttons
Color[] colorA = {Color.BLUE, Color.BLACK, Color.GREEN, Color.RED};
for(Color c : colorA){
jC.add(LeftoverExample.createButton(c));
}
}
}
看看每个西侧和东侧都有一些空间留给父级(在本例中为JLabel)占用,但按钮没有。我希望按钮也能占据这个空间
图片显示示例:
问题是由于Swing对维度使用整数值而不是双精度值造成的 考虑到这一事实,容器宽度除以其包含的
组件
(在您的情况下为JButton
对象)对象数后的剩余r可用于将第一个r组件
对象的大小增加1以进行补偿。显然,这意味着第一个r组件
对象将比其他组件
大+1,但这不应引起注意
为了更新组件
对象的宽度,我们需要访问该容器(例如JPanel
)和所有我们希望更新的组件
对象。在我的示例中,我将为此使用列表
下面是一种相应调整组件
对象大小的方法
private static void fixComponentWidths(Component container,
List<? extends Component> componentList, int componentHeight) {
if (!componentList.isEmpty()) { // Avoid possible division by zero
// get the desired component width for the container using integer division
int baseComponentWidth = container.getWidth() / componentList.size();
// find the remainder
int remainder = container.getWidth() % componentList.size();
// update all the components
for (int i = 0; i < componentList.size(); i++) {
// the component width will be the base width plus 1 iff i < remainder
int componentWidth = baseComponentWidth;
if (i < remainder) {
componentWidth++;
}
// update the maximum size
componentList.get(i).setMaximumSize(new Dimension(componentWidth, componentHeight));
}
// be sure to revalidate otherwise it may not work
container.revalidate();
}
}
这就是所需要的一切。但为了完整起见,这里有一个基于作者问题的小示例,后面是JPanel
的子类,它使用BoxLayout
来解决BoxLayout.X_轴
和BoxLayout.Y_轴
的这种行为
完整示例
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class FillExample extends JFrame {
private static final int FRAMEL_DEFAULT_WIDTH = 700;
private static final int FRAME_DEFAULT_HEIGHT = 400;
private static final int BUTTON_HEIGHT = Integer.MAX_VALUE;
private final List<JButton> buttons;
public FillExample() {
buttons = new ArrayList<>();
}
public void createAndShow() {
setTitle("Fill Example");
setSize(FRAMEL_DEFAULT_WIDTH, FRAME_DEFAULT_HEIGHT);
final JPanel buttonContainer = new JPanel();
buttonContainer.setLayout(new BoxLayout(buttonContainer, BoxLayout.X_AXIS));
for (int i = 0; i < 3; i++) {
addButtons(buttonContainer);
}
getContentPane().add(buttonContainer);
buttonContainer.addComponentListener(new ComponentListener() {
@Override
public void componentResized(ComponentEvent ce) {
fixComponentWidths(buttonContainer, buttons, BUTTON_HEIGHT);
}
@Override
public void componentMoved(ComponentEvent ce) {
}
@Override
public void componentShown(ComponentEvent ce) {
}
@Override
public void componentHidden(ComponentEvent ce) {
}
});
setVisible(true);
}
private static void fixComponentWidths(Component container, List<? extends Component> componentList, int componentHeight) {
if (!componentList.isEmpty()) {
int baseComponentWidth = container.getWidth() / componentList.size();
int remainder = container.getWidth() % componentList.size();
for (int i = 0; i < componentList.size(); i++) {
int componentWidth = baseComponentWidth;
if (i < remainder) {
componentWidth++;
}
componentList.get(i).setMaximumSize(new Dimension(componentWidth, componentHeight));
}
container.revalidate();
}
}
private void addButtons(JComponent component) {
Color[] colorA = {Color.RED, Color.BLUE, Color.BLACK, Color.GREEN};
for (Color c : colorA) {
JButton button = createButton(c);
buttons.add(button);
component.add(button);
}
}
private static JButton createButton(Color color) {
JButton button = new JButton();
button.setBackground(color);
button.setMaximumSize(new Dimension(Integer.MAX_VALUE, BUTTON_HEIGHT));
return button;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new FillExample().createAndShow();
}
});
}
}
我认为问题在于,当所有组件都具有相同的设置时,布局会试图为每个组件提供相同的空间。因此,很少的额外空间不能被平均分配到他们忽略的每个组件
也许你可以用这个
它允许您轻松地使每个组件具有相同的大小
然后,它有一个属性,允许您确定在需要时应如何分配额外的像素。在使用平板电脑时,我无法测试或分析您的代码,但通常在需要空白占位符时使用空JLabel。在发布的代码中,按钮会变宽并填充父帧。这不是你想要的吗?(旁注:从所有方法中删除static
,并创建一个LeftoverExample
的实例)为了澄清“如何在调整大小时去掉开头和结尾的空间?”这是一个非常小的水平间隙,或者你说的是垂直间隙?问题很可能是因为摆动尺寸是整数,而不是双精度。如果在调整JFrame
大小时手动计算并设置JButton
大小,则存在相同的问题。添加的组件越多,这将变得越明显。也许考虑使用JavaFX?有些人可能有一个解决方案,但即使在JButton
对象之间添加Box.createHoriztonalGlue()
也无济于事。我不知道这个布局管理器。天知道为什么这个或类似的东西没有添加到AWT/Swing库中+1对于englightneing我。@camickr,我知道你的答案将如何解决我所有的BoxLayout问题(只需将BoxLayout替换为Relative…),但你对GBL有什么建议。我真的很喜欢GBL,我认为我可以采用d.j.brown的想法,并将其应用于GBL,或者使用实用方法,或者扩展GBL并应用d.j.brown的解决方案,但我不确定从长远来看这是否是一个好主意。我已经有了一些关于GBL的实用方法(翻转布局并将最小大小的列/行添加到GBL).@BWCsemaJ,使用实用方法使约束更容易应用到一组组件是一种常见的方法。但是,从长远来看,我不认为使用组件侦听器“覆盖”布局管理器确定的大小是最好的方法。现在有两处布局代码,一处在布局管理器中,另一处在组件侦听器中。所以我的选择是定制GBL,这样所有的布局代码都在一个地方。然而,这确实是对一行组件的特殊要求。GBL比这强大得多,所以我不确定你会如何将“舍入”考虑纳入GBL的全部复杂性中。正如我在博客开始时所说的,我更喜欢使用多个简单的布局管理器,以最小的约束实现布局,而不是试图强迫布局管理器完成所有事情。IDE使用复杂的布局管理器,因为他们无法逻辑地将布局分解为更小的组件组。您喜欢的是您的选择。我知道您的答案将如何解决我的所有BoxLayout问题
-RelativeLayout并不是为解决BoxLayout问题而设计的。BoxLayout根据单个组件的大小按比例调整大小。我只是建议,如果所有组件恰好都有save siz,那么它可以模拟BoxLayout
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class FillExample extends JFrame {
private static final int FRAMEL_DEFAULT_WIDTH = 700;
private static final int FRAME_DEFAULT_HEIGHT = 400;
private static final int BUTTON_HEIGHT = Integer.MAX_VALUE;
private final List<JButton> buttons;
public FillExample() {
buttons = new ArrayList<>();
}
public void createAndShow() {
setTitle("Fill Example");
setSize(FRAMEL_DEFAULT_WIDTH, FRAME_DEFAULT_HEIGHT);
final JPanel buttonContainer = new JPanel();
buttonContainer.setLayout(new BoxLayout(buttonContainer, BoxLayout.X_AXIS));
for (int i = 0; i < 3; i++) {
addButtons(buttonContainer);
}
getContentPane().add(buttonContainer);
buttonContainer.addComponentListener(new ComponentListener() {
@Override
public void componentResized(ComponentEvent ce) {
fixComponentWidths(buttonContainer, buttons, BUTTON_HEIGHT);
}
@Override
public void componentMoved(ComponentEvent ce) {
}
@Override
public void componentShown(ComponentEvent ce) {
}
@Override
public void componentHidden(ComponentEvent ce) {
}
});
setVisible(true);
}
private static void fixComponentWidths(Component container, List<? extends Component> componentList, int componentHeight) {
if (!componentList.isEmpty()) {
int baseComponentWidth = container.getWidth() / componentList.size();
int remainder = container.getWidth() % componentList.size();
for (int i = 0; i < componentList.size(); i++) {
int componentWidth = baseComponentWidth;
if (i < remainder) {
componentWidth++;
}
componentList.get(i).setMaximumSize(new Dimension(componentWidth, componentHeight));
}
container.revalidate();
}
}
private void addButtons(JComponent component) {
Color[] colorA = {Color.RED, Color.BLUE, Color.BLACK, Color.GREEN};
for (Color c : colorA) {
JButton button = createButton(c);
buttons.add(button);
component.add(button);
}
}
private static JButton createButton(Color color) {
JButton button = new JButton();
button.setBackground(color);
button.setMaximumSize(new Dimension(Integer.MAX_VALUE, BUTTON_HEIGHT));
return button;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new FillExample().createAndShow();
}
});
}
}
import java.awt.Component;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
public class FillBoxLayoutPanel extends JPanel {
public static final int X_AXIS = BoxLayout.X_AXIS;
public static final int Y_AXIS = BoxLayout.Y_AXIS;
private final List<Component> components;
private final int direction;
private boolean layoutSet;
public FillBoxLayoutPanel(int direction) {
components = new ArrayList<>();
this.direction = direction;
setLayout(new BoxLayout(this, direction));
layoutSet = true;
addComponentListener(new ComponentListener() {
@Override
public void componentResized(ComponentEvent ce) {
adjustComponents();
}
@Override
public void componentMoved(ComponentEvent ce) {
}
@Override
public void componentShown(ComponentEvent ce) {
}
@Override
public void componentHidden(ComponentEvent ce) {
}
});
}
@Override
public void setLayout(LayoutManager mgr) {
if (layoutSet) {
throw new UnsupportedOperationException("FillPanel's layout manager cannot be changed.");
} else {
super.setLayout(mgr);
}
}
@Override
public Component add(Component comp) {
comp = super.add(comp);
components.add(comp);
return comp;
}
@Override
public Component add(Component comp, int i) {
comp = super.add(comp, i);
components.add(i, comp);
return comp;
}
private void adjustComponents() {
if (!components.isEmpty()) {
int size = direction == X_AXIS ? getWidth() : getHeight();
int baseComponentSize = size / components.size();
int remainder = size % components.size();
for (int i = 0; i < components.size(); i++) {
int componentSize = baseComponentSize;
if (i < remainder) {
componentSize++;
}
Dimension dimension;
if (direction == X_AXIS) {
dimension = new Dimension(componentSize, components.get(i).getHeight());
} else {
dimension = new Dimension(components.get(i).getWidth(), componentSize);
}
components.get(i).setMaximumSize(dimension);
}
revalidate();
}
}
}