GridLayout 表现出奇怪的行为

GridLayout is showing odd behavior

我正在尝试创建一个由 100 个正方形组成的网格。我下面的代码非常错误,我不确定为什么。

import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;

public class snake extends JFrame
{
    public static void main(String[] args)
    {
        Border whiteLine = BorderFactory.createLineBorder(Color.white);
        //-----------FRAME
        JFrame frame = new JFrame();
        frame.setSize(1000,1000);
        frame.setTitle("Snake");
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.getContentPane().setBackground(Color.black);
        frame.setVisible(true);
        frame.setLayout(new GridLayout(10,10));
        //-----------FRAME

        //-----------PANELS
        Dimension panelDimension = new Dimension(20,20);

        int counter = 0;
        JPanel[][] p = new JPanel[10][10];
        for (int i = 0; i < 10; i++)
        {
            for (int j = 0; j < 10; j++)
            {
                p[i][j] = new JPanel();
                //p[i][j].setPreferredSize(panelDimension);
                p[i][j].setBackground(Color.red);
                //p[i][j].setLocation(490,490);
                p[i][j].setBorder(whiteLine);
                p[i][j].setVisible(true);

                frame.getContentPane().add(p[i][j]);

                counter+=1;
            }
        }
       System.out.println("counter: " + counter);


    }
}

当我 运行 这样的代码时,它会显示一个由 2 列组成的网格,第一列有 7 行,第二列有 6 行。有时它甚至会显示其他不正确的列数和行数。我不确定为什么它不创建 10 行 10 列的网格。

您遇到了几个问题,包括:

  • 在 JFrame 上调用 setVisible(true) before 添加组件,然后在顶层调用 pack() window。这可能导致我们的 GUI 中的不稳定定位组件甚至 GUI 仍然是空的
  • 在添加组件之后和将其设置为可见之前不在 JFrame 上调用 pack()
  • 正在设置 JFrame 的大小。让布局管理器、容器和组件为你做这件事(这就是调用 pack() 的目的)
  • 将其设置为错误的大小,一个“完美的正方形”,忽略 OS 添加的菜单栏,
  • ...

例如:

package foo01;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;

@SuppressWarnings("serial")
public class SnakePanel extends JPanel {
    private static final int CELL_WIDTH = 80;
    private static final Dimension CELL_DIMENSION = new Dimension(CELL_WIDTH, CELL_WIDTH);
    private static final int COLUMNS = 10;
    private static final int GAP = 2;
    private static final Color BG_COLOR = Color.WHITE;
    private static final Color CELL_COLOR = Color.RED;

    public SnakePanel() {
        setBackground(BG_COLOR);
        
        // add a white line around the grid
        setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
        
        // create a grid with gaps that show the background (white) color
        setLayout(new GridLayout(COLUMNS, COLUMNS, GAP, GAP));

        for (int row = 0; row < COLUMNS; row++) {
            for (int col = 0; col < COLUMNS; col++) {
                JPanel cell = new JPanel(); // create a new cell
                cell.setPreferredSize(CELL_DIMENSION); // cheating here. Better to override getPreferredSize()
                cell.setBackground(CELL_COLOR);
                add(cell);
                
                // give the cell JPanel some simple behavior:
                cell.addMouseListener(new MyMouse(col, row));
            }
        }
    }

    private class MyMouse extends MouseAdapter {
        private int col;
        private int row;

        public MyMouse(int col, int row) {
            this.col = col;
            this.row = row;
        }
        
        @Override
        public void mousePressed(MouseEvent e) {
            System.out.printf("Mouse pressed row and column: [%d, %d]%n", row, col);
        }
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            // create the main JPanel
            SnakePanel snakePanel = new SnakePanel();

            // create the JFrame
            JFrame frame = new JFrame("Snake");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            
            // add the main JPanel to the JFrame
            frame.add(snakePanel);
            
            // pack the JFrame -- tells the layout managers to do their things
            frame.pack();
            
            // if we want to center the GUI:
            frame.setLocationRelativeTo(null);
            
            // only *now* do we display the GUI
            frame.setVisible(true);
        });
    }
}

代码的一些注释:

传递给 SwingUtilities.invokeLater(...) 方法的 Runnable 中的任何代码都在 Swing 事件线程上调用,这在创建 Swing GUI 时是明智的做法

SwingUtilities.invokeLater(() -> {
    // ....
});

首先,创建由 JFrame 持有的主 JPanel:

SnakePanel snakePanel = new SnakePanel();

然后创建 JFrame,添加 JPanel 并调用 pack()。 pack 调用告诉布局管理器做这些事情,在容器中布置组件,根据它们的首选大小和布局调整大小:

JFrame frame = new JFrame("Snake");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(snakePanel);
frame.pack();

如果我们想让 GUI 居中:

frame.setLocationRelativeTo(null);

现在我们只显示 GUI

frame.setVisible(true);