4

我在使用 GridLayout 时遇到问题,并且整个 JPanel 没有被填充。我有一个 N * M Grid,我用 N * M Tiles 填充它(它们扩展了 JPanel)。添加所有这些图块后,JPanel 的底部和右侧仍有空间。我认为 GridLayout 应该填满整个 JPanel,并使其中的所有内容大小均匀?

编辑:我不想发布所有代码,因为有多个类。想也许有一个简单的解决方案。

public class MainFrame extends JFrame {

    private static final long serialVersionUID = 3985493842977428355L;
    private final int FRAME_HEIGHT = 800;
    private final int FRAME_WIDTH = 700;

    private MainPanel mainPanel;

    public MainFrame() {
        super("Test");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(FRAME_HEIGHT, FRAME_WIDTH);
        setLocationRelativeTo(null);    
        mainPanel = new MainPanel();
        getContentPane().add(mainPanel);
        setVisible(true);
    }
}

public class MainPanel extends JPanel {
    /**
     * 
     */
    private static final long serialVersionUID = 3421071253693531472L;

    private RoutePanel routePanel;
    private ControlPanel controlPanel;
    private GridBagConstraints gridBagConstraints;
    private GridBagLayout gridBagLayout;

    public MainPanel() {
        routePanel = new RoutePanel();
        controlPanel = new ControlPanel();
        setBackground(Color.black);
        gridBagLayout = new GridBagLayout();
        setLayout(gridBagLayout);
        gridBagConstraints = new GridBagConstraints();
        createLayout();
    }

    private void createLayout() {
        gridBagConstraints.weightx = 1;
        gridBagConstraints.weighty = 1;
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = GridBagConstraints.NORTH;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagLayout.setConstraints(routePanel, gridBagConstraints);
        add(routePanel);

        gridBagConstraints.weightx = 1;
        gridBagConstraints.weighty = .05;
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = GridBagConstraints.SOUTH;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagLayout.setConstraints(controlPanel, gridBagConstraints);
        add(controlPanel);
    }
}

public class RoutePanel extends JPanel{
    /**
     * 
     */
    private static final long serialVersionUID = 4366703088208967594L;
    private final int GRID_ROWS = 30;
    private final int GRID_COLS = 47;


    public RoutePanel() {
        setLayout(new GridLayout(GRID_ROWS, GRID_COLS, 0, 0));
        for(int i = 0; i < GRID_ROWS * GRID_COLS; i++) {
            add(new Tile());
        }
        setBackground(Color.orange);
    }
}

public ControlPanel() {

    }

public Tile() {
        setBackground(Color.gray);
        Border blackline;
        blackline = BorderFactory.createLineBorder(Color.black);
        setBorder(blackline);
    }
4

2 回答 2

7

由于您没有发布代码,我们被迫猜测(这不好)。

我的猜测:您可能正在设置某些组件或 GUI 的大小或首选大小,这会导致 GUI 边缘出现间隙。

解决方案:不要这样做。pack()当在 GUI 上调用时,让组件通过其首选大小和布局管理器来调整自己的大小。

如需更多帮助,请创建并发布您的sscce。对于这样的问题,代码确实是强制性的,实际上你没有发布你的代码让我有点惊讶。


编辑 1

例如,请注意 GUI 生成的差异:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;

import javax.swing.*;

public class GapExample {
   private static final int M = 5;
   private static final int N = 6;
   private static final int PREF_W = 700;
   private static final int PREF_H = 500;

   @SuppressWarnings("serial")
   private static void createAndShowGui() {

      // *** attempt 1: set preferredSize of the mainPanel JPanel ***
      JPanel mainPanel = new JPanel(new GridLayout(M, N));
      mainPanel.setBorder(BorderFactory.createLineBorder(Color.red));
      mainPanel.setPreferredSize(new Dimension(PREF_W, PREF_H));

      for (int i = 0; i < M; i++) {
         for (int j = 0; j < N; j++) {
            String text = String.format("Foo [%d, %d]", i, j);
            JLabel label = new JLabel(text, SwingConstants.CENTER);
            label.setBorder(BorderFactory.createLineBorder(Color.blue));
            mainPanel.add(label);
         }
      }
      JOptionPane.showMessageDialog(null, mainPanel,
            "Attempt 1, setPreferredSize of mainPane",
            JOptionPane.PLAIN_MESSAGE);

      // *** attempt 2: override the getPreferredSize of the JLabel cells in the
      // grid ***
      mainPanel = new JPanel(new GridLayout(M, N));
      mainPanel.setBorder(BorderFactory.createLineBorder(Color.red));
      final Dimension labelPrefSize = new Dimension(PREF_W / N, PREF_H / M);
      for (int i = 0; i < M; i++) {
         for (int j = 0; j < N; j++) {
            String text = String.format("Foo [%d, %d]", i, j);
            JLabel label = new JLabel(text, SwingConstants.CENTER) {
               @Override
               public Dimension getPreferredSize() {
                  return labelPrefSize;
               }
            };
            label.setBorder(BorderFactory.createLineBorder(Color.blue));
            mainPanel.add(label);
         }
      }
      JOptionPane
            .showMessageDialog(null, mainPanel,
                  "Attempt 2, override getPreferredSize of the JLabel cells in the grid",
                  JOptionPane.PLAIN_MESSAGE);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

在第二个对话框中,我让 JLabels 返回一个首选大小,并让容纳它们的容器设置最佳大小以显示 GUI,您将在第二次尝试时,网格更适合。

这显示了第一次(错误的)尝试内部和外部组件之间的间隙:

在此处输入图像描述

第二次(好的)尝试没有显示出这样的差距:

在此处输入图像描述

于 2013-07-13T03:44:34.630 回答
3

GridLayout 应该填满整个 JPanel,并使其中的所有内容大小均匀

这两个是相反的力量,只有当面板宽度是列宽的倍数时,经理才能同时做这两个:

panel.width == column.width * column.count

如果这是不可能的,它有利于相等大小的约束,通过将子级调整为最大宽度以使它们相等并将整个块定位在父级的中心,从而暴露父级的背景。基本上,您会看到整数数学的效果 :-)


至于另一个答案中的讨论

首先:我对 setXXSize 的看法是众所周知的——在应用程序代码中使用它总是错误的解决。

下一个最近的解决方案似乎覆盖了 getXXSize:如果返回任意硬编码的固定大小,它只比调用 setXXSize 稍微好一点(不传播最坏的做法:):返回值应该与内部状态相关。它应该保持 super 的内部计算(如果有的话)完整并且可以修改它。严格来说,它应该遵守 super 的合同,该合同确实规定(在 setXXSize 中有点隐藏)通过 setXXSize 应用的维度优先

将此组件的首选大小设置为常量值。对 getPreferredSize 的后续调用将始终返回此值。

在代码中:

// always wrong in application code
someComponent.setPreferredSize(labelPrefSize);

// suboptimal: arbitrary hard-coded fixed size 
@Override
public Dimension getPreferredSize() {
    return new Dimension(labelPrefSize);
}

// good: fixed value based on internal state
@Override
public Dimension getPreferredSize() {
    return new Dimension(labelPrefSize);
}
// the size has some meaning f.i. when painting
@Override
protected void paintComponent(Graphics g) {
    g.drawRect(0, 0, labelPrefSize.width, labelPrefSize.height);
}

// good: prefsize relative to super's calculation
@Override
public Dimension getPreferredSize() {
    Dimension labelPrefSize = super.getPreferredSize();
    int maxSide = Math.max(labelPrefSize.width, labelPrefSize.height);
    labelPrefSize.setSize(maxSide, maxSide);
    return labelPrefSize;
}

// better: prefsize relative to default calculation
// _and_ comply to super's contract
@Override
public Dimension getPreferredSize() {
    Dimension labelPrefSize = super.getPreferredSize();
    if (isPreferredSizeSet()) return labelPrefSize;
    // any of the good thingies ...
}

不过,理想情况下,您根本不会触及尺寸提示的内部计算。相反,请使用允许微调布局以完全适合您的需求的 LayoutManager。请记住:经理会接受提示,并且可以随意调整大小,但她还是喜欢 :-)

更新

编辑上面试图强调“与内部状态相关”的东西:尺寸提示应该返回他们自己孤立的、以自我为中心的要求,而不关心上下文。因此,压倒一切地返回满足外部需求的东西是次优的。

于 2013-07-13T15:35:39.793 回答