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?
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:
When you expand the frame, the GUI looks like this:
Oracle has a helpful tutorial, Creating a GUI With Swing. 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.
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.
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);
}
}
}