英文:
How to center table cells and remove a gap near the java swing JTable?
问题
JTable预计是Pac-Man游戏板的实现,因此表格的单元格是方形的。还应考虑表格的可伸缩性。问题在于,如果在JPanel中使用BorderLayout,表格是可伸缩的,但靠左对齐,如果未将BorderLayout传递给JPanel构造函数,则表格居中但不可伸缩。
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("表格");
}
Game(String title) {
super(title);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
JPanel p = new JPanel(new BorderLayout());
p.setBackground(Color.BLACK);
BoardView b = new BoardView(new Board(36, 28));
p.add(b);
add(p);
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.WHITE);
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
int size = Math.min(getHeight() / getRowCount(), getWidth() / getColumnCount());
setRowHeight(size);
for (int i = 0; i < getColumnCount(); i++) {
TableColumn column = getColumnModel().getColumn(i);
column.setMaxWidth(size);
}
}
});
}
}
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];
}
@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);
}
}
}
请问是否需要进一步解决此问题的指导?
英文:
The JTable is expected to be the implementation of the Pac-Man game board, therefore the cells of the table are squared. The scalability of the table should also be considered. The problem is that if in the JPanel I use BorderLayout the table is scalable, but aligned to the left and when the BorderLayout is not passed to the JPanel constructor, the table is centered but not scalable.
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("Table");
}
Game(String title) {
super(title);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
JPanel p = new JPanel(new BorderLayout());
p.setBackground(Color.BLACK);
BoardView b = new BoardView(new Board(36, 28));
p.add(b);
add(p);
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.WHITE);
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
int size = Math.min(getHeight() / getRowCount(), getWidth() / getColumnCount());
setRowHeight(size);
for (int i = 0; i < getColumnCount(); i++) {
TableColumn column = getColumnModel().getColumn(i);
column.setMaxWidth(size);
}
}
});
}
}
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];
}
@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);
}
}
}
Could someone please provide guidance on how to resolve this issue?
答案1
得分: 1
# 介绍
我仍然会创建一个绘制`JPanel`并绘制方框,而不是像这样篡改`JTable`。
花了一些时间,但我修改了您发布的代码,创建了以下GUI:
[![表格1][1]][1]
当你扩展窗口时,GUI看起来像这样:
[![表格2][2]][2]
# 解释
Oracle有一个有用的教程,[使用Swing创建GUI](https://docs.oracle.com/javase/tutorial/uiswing/index.html)。跳过使用NetBeans IDE学习Swing部分。
所有Swing应用程序都必须以对`SwingUtilities`的`invokeLater`方法的调用开始。这个方法确保Swing组件在[事件分派线程](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html)上创建和执行。
我修改了您的代码,使用了`JFrame`、`JPanel`和`JTable`,而不是扩展任何东西。这使我更加灵活,允许我分开我的关注,专注于GUI的一部分。
`JPanel`使用`BorderLayout`。通过调整空边框的边距,我能够将`JTable`居中放置在`JPanel`中。
在构造`JPanel`和`JTable`时,我必须在构建GUI之前计算`JTable`和`JPanel`的大小。这就是为什么`JPanel`有一个大小计算方法,`ComponentAdapter`有一个单独的大小计算的原因。
我创建了一个单独的`ComponentAdapter`类,只是为了将代码从`JPanel`类中提取出来。
# 代码
以下是完整的可运行代码。我将其他类作为内部类,以便将代码一次性发布。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
public class JTableLayout {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new JTableLayout("Table");
}
});
}
private JTable table;
JTableLayout(String title) {
Board board = new Board(36, 28);
JFrame frame = new JFrame(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BoardView(board).getPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class BoardView {
private Board board;
private JPanel panel;
public BoardView(Board board) {
this.board = board;
this.panel = createJTablePanel();
}
private JPanel createJTablePanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
table = new JTable(board);
table.setTableHeader(null);
table.setBackground(Color.WHITE);
table.setPreferredSize(calculateBoardDimension(table));
panel.add(table);
panel.addComponentListener(new PanelListener(table, board));
return panel;
}
private Dimension calculateBoardDimension(JTable table) {
int totalHeight = table.getRowHeight() * board.getRowCount();
int totalWidth = table.getRowHeight() * board.getColumnCount();
for (int i = 0; i < table.getColumnCount(); i++) {
TableColumn column = table.getColumnModel().getColumn(i);
column.setMaxWidth(table.getRowHeight());
}
return new Dimension(totalWidth, totalHeight);
}
public JPanel getPanel() {
return panel;
}
}
public class PanelListener extends ComponentAdapter {
private Board board;
private JTable table;
public PanelListener(JTable table, Board board) {
this.table = table;
this.board = board;
}
@Override
public void componentResized(ComponentEvent event) {
JPanel panel = (JPanel) event.getSource();
Dimension d = panel.getSize();
int height = d.height / board.getRowCount();
int width = d.width / board.getColumnCount();
boolean widthLarger = true;
int size = height;
if (size > width) {
size = width;
widthLarger = false;
}
for (int i = 0; i < table.getColumnCount(); i++) {
TableColumn column = table.getColumnModel().getColumn(i);
column.setMaxWidth(size);
}
table.setRowHeight(size);
Border border = panel.getBorder();
Insets insets = (Insets) border.getBorderInsets(panel);
if (widthLarger) {
int boardWidth = board.getColumnCount() * size;
int margin = (d.width - boardWidth) / 2;
panel.setBorder(BorderFactory.createEmptyBorder(insets.top,
margin, insets.bottom, margin));
} else {
int boardHeight = board.getRowCount() * size;
int margin = (d.height - boardHeight) / 2;
panel.setBorder(BorderFactory.createEmptyBorder(margin,
insets.left, margin, insets.right));
}
}
}
public class Board extends AbstractTableModel {
private static final long serialVersionUID = 1L;
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];
}
@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);
}
}
}
<details>
<summary>英文:</summary>
# Introduction
I'd still create a drawing `JPanel` and draw the boxes rather than bastardize a `JTable` like this.
It took me a while, but I modified the code you posted enough to create the following GUI:
[![Table 1][1]][1]
When you expand the frame, the GUI looks like this:
[![Table 2][2]][2]
# Explanation
Oracle has a helpful tutorial, [Creating a GUI With Swing](https://docs.oracle.com/javase/tutorial/uiswing/index.html). Skip the Learning Swing with the NetBeans IDE section.
All Swing applications must start with a call to the `SwingUtilities` `invokeLater` method. This method ensures that the Swing components are created and executed on the [Event Dispatch Thread](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html).
I modified your code to use a `JFrame`, `JPanel`, and `JTable`, rather than extend anything. This gave me more flexibility, allowed me to separate my concerns, and focus on one part of the GUI at a time.
The `JPanel` uses a `BorderLayout`. By adjusting the margins of an empty border, I was able to center the `JTable` in the `JPanel`.
When constructing the `JPanel` and the `JTable`, I had to calculate the size of the `JTable` and `JPanel` before I could construct the GUI. This is why the `JPanel` has a size calculation method and the `ComponentAdapter` has a separate size calculation.
I created a separate `ComponentAdapter` class just so I could get the code out of the `JPanel` class.
# Code
Here's the complete runnable code. I made the additional classes inner classes so I could post the code as one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
public class JTableLayout {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new JTableLayout("Table");
}
});
}
private JTable table;
JTableLayout(String title) {
Board board = new Board(36, 28);
JFrame frame = new JFrame(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BoardView(board).getPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class BoardView {
private Board board;
private JPanel panel;
public BoardView(Board board) {
this.board = board;
this.panel = createJTablePanel();
}
private JPanel createJTablePanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
table = new JTable(board);
table.setTableHeader(null);
table.setBackground(Color.WHITE);
table.setPreferredSize(calculateBoardDimension(table));
panel.add(table);
panel.addComponentListener(new PanelListener(table, board));
return panel;
}
private Dimension calculateBoardDimension(JTable table) {
int totalHeight = table.getRowHeight() * board.getRowCount();
int totalWidth = table.getRowHeight() * board.getColumnCount();
for (int i = 0; i < table.getColumnCount(); i++) {
TableColumn column = table.getColumnModel().getColumn(i);
column.setMaxWidth(table.getRowHeight());
}
return new Dimension(totalWidth, totalHeight);
}
public JPanel getPanel() {
return panel;
}
}
public class PanelListener extends ComponentAdapter {
private Board board;
private JTable table;
public PanelListener(JTable table, Board board) {
this.table = table;
this.board = board;
}
@Override
public void componentResized(ComponentEvent event) {
JPanel panel = (JPanel) event.getSource();
Dimension d = panel.getSize();
int height = d.height / board.getRowCount();
int width = d.width / board.getColumnCount();
boolean widthLarger = true;
int size = height;
if (size > width) {
size = width;
widthLarger = false;
}
for (int i = 0; i < table.getColumnCount(); i++) {
TableColumn column = table.getColumnModel().getColumn(i);
column.setMaxWidth(size);
}
table.setRowHeight(size);
Border border = panel.getBorder();
Insets insets = (Insets) border.getBorderInsets(panel);
if (widthLarger) {
int boardWidth = board.getColumnCount() * size;
int margin = (d.width - boardWidth) / 2;
panel.setBorder(BorderFactory.createEmptyBorder(insets.top,
margin, insets.bottom, margin));
} else {
int boardHeight = board.getRowCount() * size;
int margin = (d.height - boardHeight) / 2;
panel.setBorder(BorderFactory.createEmptyBorder(margin,
insets.left, margin, insets.right));
}
}
}
public class Board extends AbstractTableModel {
private static final long serialVersionUID = 1L;
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];
}
@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);
}
}
}
[1]: https://i.stack.imgur.com/UAcyZ.png
[2]: https://i.stack.imgur.com/fxbuE.png
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论