如何让Java Swing JTable居中且可缩放?解决单元格布局问题
解决JTable实现Pac-Man棋盘的居中与缩放问题
核心修改思路
要同时实现单元格正方形缩放+棋盘居中,需要结合嵌套布局调整JTable的容器层级,并修正单元格尺寸的计算逻辑:
- 嵌套布局实现居中:在外层
BorderLayout容器中,嵌套一个使用FlowLayout.CENTER的JPanel,将JTable放入这个居中面板,确保JTable始终在容器中央。 - 基于父容器尺寸计算单元格:不再用JTable自身尺寸,而是取父容器的可用空间来计算最大正方形单元格大小,避免JTable被过度拉伸。
- 固定列宽与更新首选尺寸:强制列宽等于单元格大小,同时更新JTable的首选尺寸,触发父容器重新布局,保持棋盘居中且单元格缩放。
修改后的完整代码
import javax.swing.*; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import java.awt.*; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; public class Game extends JFrame { public static void main(String[] args) { new Game("Pac-Man Board"); } Game(String title) { super(title); setDefaultCloseOperation(EXIT_ON_CLOSE); setLayout(new BorderLayout()); // 外层容器,黑色背景 JPanel outerPanel = new JPanel(new BorderLayout()); outerPanel.setBackground(Color.BLACK); // 居中容器,负责让JTable始终居中 JPanel centerPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); centerPanel.setBackground(Color.BLACK); BoardView boardView = new BoardView(new Board(36, 28)); centerPanel.add(boardView); outerPanel.add(centerPanel, BorderLayout.CENTER); add(outerPanel); pack(); setLocationRelativeTo(null); setVisible(true); } public class BoardView extends JTable { private Board board; public BoardView(Board board) { super(board); this.board = board; setTableHeader(null); setBackground(Color.BLACK); // 关闭自动列调整,避免列宽被强制拉伸 setAutoResizeMode(JTable.AUTO_RESIZE_OFF); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { Container parent = getParent(); if (parent == null) return; int rows = getRowCount(); int cols = getColumnCount(); if (rows == 0 || cols == 0) return; // 基于父容器可用空间计算最大正方形单元格尺寸 int cellSize = Math.min(parent.getHeight() / rows, parent.getWidth() / cols); cellSize = Math.max(cellSize, 1); // 防止容器过小时尺寸为0 // 设置行高与列宽 setRowHeight(cellSize); for (int i = 0; i < cols; i++) { TableColumn column = getColumnModel().getColumn(i); column.setWidth(cellSize); column.setMinWidth(cellSize); column.setMaxWidth(cellSize); } // 更新JTable首选尺寸,让居中容器重新布局 setPreferredSize(new Dimension(cols * cellSize, rows * cellSize)); parent.revalidate(); } }); } // 重写该方法,确保滚动视图(如果使用)适配首选尺寸 @Override public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } } public class Board extends AbstractTableModel { private int rows; private int columns; private int[][] boardData; public Board(int rows, int columns) { this.rows = rows; this.columns = columns; this.boardData = new int[rows][columns]; // 初始化棋盘数据示例(比如墙体) for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { boardData[i][j] = (i == 0 || i == rows-1 || j ==0 || j == columns-1) ? 1 : 0; } } } @Override public int getRowCount() { return rows; } @Override public int getColumnCount() { return columns; } @Override public Object getValueAt(int rowIndex, int columnIndex) { return boardData[rowIndex][columnIndex]; } @Override public void setValueAt(Object value, int rowIndex, int columnIndex) { boardData[rowIndex][columnIndex] = (int) value; fireTableCellUpdated(rowIndex, columnIndex); } // 重写该方法,让JTable可以自定义单元格渲染 @Override public Class<?> getColumnClass(int columnIndex) { return Integer.class; } } }
关键修改点说明
- 布局层级调整:新增
centerPanel使用FlowLayout.CENTER,确保JTable在任何容器尺寸下都居中显示。 - 单元格尺寸计算:改用父容器的尺寸计算单元格大小,避免JTable被拉伸后出现空白区域。
- 列宽固定:设置列的最小、最大、实际宽度均为单元格尺寸,保证单元格严格正方形。
- 首选尺寸更新:修改JTable的
preferredSize后调用parent.revalidate(),触发居中容器重新布局,让JTable始终以最佳尺寸居中。
内容的提问来源于stack exchange,提问作者stov.ft
相关产品推荐
相关产品推荐

