在 Java 中使用 JPanel 的数独板
Posted
技术标签:
【中文标题】在 Java 中使用 JPanel 的数独板【英文标题】:Sudoku Board Using JPanels in Java 【发布时间】:2012-10-04 20:53:48 【问题描述】:我知道已经有几篇关于数独相关问题的帖子,但我不确定其中是否有我正在寻找的内容......
我正在尝试使用 JPanels 和 JTextfields 在 Java 中构建一个空的数独板。我还需要使用另一个 JPanel 在右侧创建一个菜单。
棋盘本身是一个 9 x 9 的正方形,分成 9 个 3x3 的正方形。请注意,每个较小的正方形都由比常规正方形间边界更重的边界隔开。每个方块都是一个文本字段。编写程序,使文本字段中没有任何内容。用户可以根据需要在文本字段中输入,如果输入,数字将显示出来。侧面有四个按钮,可让您解决、获得新谜题、获得提示或重置谜题。
任何想法都会很棒。我无法理解如何嵌套 for 循环来创建板。这是我的代码...
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
public class ArrayTest extends JFrame
public ArrayTest()
JPanel board = new JPanel(new GridLayout(9, 9));
add(board);
JPanel[][] squares = new JPanel[9][9];
Border border = BorderFactory.createLineBorder(Color.BLACK);
for (int row = 1; row < 9; row++)
for (int col = 1; col < 9; col++)
squares[row][col] = new JPanel();
board.add(squares[row][col]);
JPanel menu = new JPanel();
menu.add(new JButton("Reset"));
menu.add(new JButton("Hint"));
menu.add(new JButton("Solve"));
menu.add(new JButton("New Puzzle"));
add(menu);
public static void main(String[] args)
// TODO Auto-generated method stub
/** Create a frame and set its properties*/
JFrame frame = new ArrayTest();
frame.setTitle("Sudoku");
frame.setSize(600, 600);
frame.setLocationRelativeTo(null); //Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【问题讨论】:
数组索引通常从 0 开始;) 请查看example,了解当前问题。 【参考方案1】:首先,我会使用某种模型来控制“虚拟”板中的值,这会将逻辑与 UI 分开,并允许更改其中一个而不会对另一个产生不利影响。
我会为模型提供适当的事件,以允许在模型更改时更新 UI,并为每个字段提供根据需要更新模型的方法。
然后我会将问题简化为最小的概念组件,即子板,并生成 UI 以尽可能抽象的方式表示它。这允许重复使用并有助于调试,就像一块板有问题一样,您可以在一个地方修复它。
public class Sudoku
public static void main(String[] args)
new Sudoku();
public Sudoku()
EventQueue.invokeLater(new Runnable()
@Override
public void run()
try
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
catch (ClassNotFoundException ex)
catch (InstantiationException ex)
catch (IllegalAccessException ex)
catch (UnsupportedLookAndFeelException ex)
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new SudokuBoard());
frame.add(new MenuPane(), BorderLayout.AFTER_LINE_ENDS);
frame.pack();
frame.setVisible(true);
);
public class MenuPane extends JPanel
public MenuPane()
setBorder(new EmptyBorder(4, 4, 4, 4));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(new JButton("Solve"), gbc);
gbc.gridy++;
add(new JButton("New"), gbc);
gbc.gridy++;
add(new JButton("Hint"), gbc);
gbc.gridy++;
add(new JButton("Reset"), gbc);
public class SudokuBoard extends JPanel
public static final int ROWS = 3;
public static final int COLUMNS = 3;
private SubBoard[] subBoards;
public SudokuBoard()
setBorder(new EmptyBorder(4, 4, 4, 4));
subBoards = new SubBoard[ROWS * COLUMNS];
setLayout(new GridLayout(ROWS, COLUMNS, 2, 2));
for (int row = 0; row < ROWS; row++)
for (int col = 0; col < COLUMNS; col++)
int index = (row * ROWS) + col;
SubBoard board = new SubBoard();
board.setBorder(new CompoundBorder(new LineBorder(Color.GRAY, 3), new EmptyBorder(4, 4, 4, 4)));
subBoards[index] = board;
add(board);
public class SubBoard extends JPanel
public static final int ROWS = 9;
public static final int COLUMNS = 9;
private JTextField[] fields;
public SubBoard()
setLayout(new GridLayout(ROWS, COLUMNS, 2, 2));
fields = new JTextField[ROWS * COLUMNS];
for (int row = 0; row < ROWS; row++)
for (int col = 0; col < COLUMNS; col++)
int index = (row * COLUMNS) + col;
JTextField field = new JTextField(4);
fields[index] = field;
// field.setText(Integer.toString(index));
add(field);
更新
要将文本字段限制为仅允许输入数值,您可以查看JTextField limiting character amount input and accepting numeric only 了解一些想法
已更新(使用二维数组)
这是一个使用 2D 数组的实现,它还对子板进行分组,以便每个 3x3 字段的网格都有自己的板...
public class Sudoku
public static void main(String[] args)
new Sudoku();
public Sudoku()
EventQueue.invokeLater(new Runnable()
@Override
public void run()
try
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
catch (ClassNotFoundException ex)
catch (InstantiationException ex)
catch (IllegalAccessException ex)
catch (UnsupportedLookAndFeelException ex)
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new SudokuBoard());
frame.add(new MenuPane(), BorderLayout.AFTER_LINE_ENDS);
frame.pack();
frame.setVisible(true);
);
public class MenuPane extends JPanel
public MenuPane()
setBorder(new EmptyBorder(4, 4, 4, 4));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(new JButton("Solve"), gbc);
gbc.gridy++;
add(new JButton("New"), gbc);
gbc.gridy++;
add(new JButton("Hint"), gbc);
gbc.gridy++;
add(new JButton("Reset"), gbc);
public class SudokuBoard extends JPanel
public static final int ROWS = 3;
public static final int COLUMNS = 3;
private SubBoard[][] subBoards;
public SudokuBoard()
setBorder(new EmptyBorder(4, 4, 4, 4));
subBoards = new SubBoard[ROWS][COLUMNS];
setLayout(new GridLayout(ROWS, COLUMNS, 2, 2));
for (int row = 0; row < ROWS; row++)
for (int col = 0; col < COLUMNS; col++)
int index = (row * ROWS) + col;
SubBoard board = new SubBoard();
board.setBorder(new CompoundBorder(new LineBorder(Color.GRAY, 3), new EmptyBorder(4, 4, 4, 4)));
subBoards[row][col] = board;
add(board);
public class SubBoard extends JPanel
public SubBoard()
setLayout(new GridLayout(3, 3, 2, 2));
for (int index = 0; index < 3*3; index++)
add(new ChildBoard(3, 3));
public class ChildBoard extends JPanel
private JTextField[][] fields;
public ChildBoard(int rows, int cols)
setBorder(new LineBorder(Color.LIGHT_GRAY));
setLayout(new GridLayout(rows, cols, 2, 2));
fields = new JTextField[rows][cols];
for (int row = 0; row < rows; row++)
for (int col = 0; col < cols; col++)
JTextField field = new JTextField(4);
fields[row][col] = field;
add(field);
或者,如果您想尝试将所有字段保留在一个***引用中,您可以执行类似...
public class SubBoard extends JPanel
private JTextField[][] fields;
public SubBoard()
setLayout(new GridLayout(3, 3, 2, 2));
fields = new JTextField[9][9];
for (int row = 0; row < 9; row++)
for (int col = 0; col < 9; col++)
fields[row][col] = new JTextField(4);
for (int row = 0; row < 3; row++)
for (int col = 0; col < 3; col++)
int startRow = row * 3;
int startCol = col * 3;
add(new ChildBoard(3, 3, fields, startRow, startCol));
public class ChildBoard extends JPanel
public ChildBoard(int rows, int cols, JTextField[][] fields, int startRow, int startCol)
setBorder(new LineBorder(Color.LIGHT_GRAY));
setLayout(new GridLayout(rows, cols, 2, 2));
for (int row = 0; row < rows; row++)
for (int col = 0; col < cols; col++)
JTextField field = fields[startRow + row][startCol + col];
fields[row][col] = field;
add(field);
单类更新
好吧,与其子类化,不如简单地使用几种方法来创建板的每个单独部分,您可以从中重复调用...
记住、减少和重用。
public class Sudoku
public static void main(String[] args)
new Sudoku();
public Sudoku()
EventQueue.invokeLater(new Runnable()
@Override
public void run()
try
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
catch (ClassNotFoundException ex)
catch (InstantiationException ex)
catch (IllegalAccessException ex)
catch (UnsupportedLookAndFeelException ex)
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new SudokuBoard());
frame.add(new MenuPane(), BorderLayout.AFTER_LINE_ENDS);
frame.pack();
frame.setVisible(true);
);
public class MenuPane extends JPanel
public MenuPane()
setBorder(new EmptyBorder(4, 4, 4, 4));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(new JButton("Solve"), gbc);
gbc.gridy++;
add(new JButton("New"), gbc);
gbc.gridy++;
add(new JButton("Hint"), gbc);
gbc.gridy++;
add(new JButton("Reset"), gbc);
public class SudokuBoard extends JPanel
public static final int GRID_ROWS = 3;
public static final int GRID_COLUMNS = 3;
public static final int BOARD_ROWS = 9;
public static final int BOARD_COLUMNS = 9;
private JTextField fields[][];
public SudokuBoard()
setBorder(new EmptyBorder(4, 4, 4, 4));
fields = new JTextField[GRID_ROWS * BOARD_ROWS][GRID_COLUMNS * BOARD_COLUMNS];
setLayout(new GridLayout(GRID_ROWS, GRID_COLUMNS, 2, 2));
for (int row = 0; row < GRID_ROWS; row++)
for (int col = 0; col < GRID_COLUMNS; col++)
int startRow = row * GRID_ROWS;
int startCol = col * GRID_COLUMNS;
add(createBoard(fields, startRow, startCol));
protected JPanel createBoard(JTextField fiels[][], int startRow, int startCol)
JPanel panel = new JPanel(new GridLayout(3, 3, 2, 2));
panel.setBorder(new CompoundBorder(new LineBorder(Color.DARK_GRAY, 2), new EmptyBorder(2, 2, 2, 2)));
for (int row = 0; row < 3; row++)
for (int col = 0; col < 3; col++)
int rowIndex = (startRow + row) * 3;
int colIndex = (startCol + col) * 3;
panel.add(createSubBoard(fields, rowIndex, colIndex));
return panel;
protected JPanel createSubBoard(JTextField[][] fields, int startRow, int startCol)
JPanel panel = new JPanel(new GridLayout(3, 3, 2, 2));
panel.setBorder(new CompoundBorder(new LineBorder(Color.GRAY, 2), new EmptyBorder(2, 2, 2, 2)));
populateFields(fields, startRow, startCol);
for (int row = 0; row < 3; row++)
for (int col = 0; col < 3; col++)
panel.add(fields[row + startRow][col + startCol]);
return panel;
protected void populateFields(JTextField[][] fields, int startRow, int startCol)
for (int row = startRow; row < startRow + 3; row++)
for (int col = startCol; col < startCol + 3; col++)
fields[row][col] = new JTextField(4);
【讨论】:
这看起来非常令人印象深刻,但我们在课堂上还没有那么先进。它只是 jpanels 上的一个二维文本框数组,右侧有一个菜单。 更新为使用 2D 数组并在每个 3x3 网格周围应用一块板:P 感谢您的所有帮助。现在,当我尝试实现 main 方法时,出现错误: JFrame frame = new SudBoard(); (“无法将 SudBoard 转换为 JFrame。”) SubBoard 是JPanel
而不是 JFrame
,它们是不同的类。您需要将SubBoard
添加到JFrame
,就像我的Sudoku
类在它的构造函数中所做的那样
请看本页底部的代码。我需要把这一切都放在一个班级里,所以我试图附加你所拥有的。我的课程名为“SudBoard”,我在底部有 main 方法。【参考方案2】:
我看到的几件事:
我不认为你现在想要 9x9 JPanel
s,而是 9x9 JTextField
s。您可能需要 3x3 JPanel
s,这样您就可以使每个部分的边框更粗。明确地列出这些而不是尝试循环执行可能更容易。
您的循环计数器(和数组索引)应该从 0 开始,而不是 1。按照现在的方式,循环只会执行 8 次。
您将要跟踪每一行、每一列以及每个 3x3 子组中的值。行和列很简单,就像您在 2D 数组中一样。您可能会考虑使用另一个数组来保存每个 3x3 区域中的值。这样可以在您需要时更轻松地扫描这些值,并且如果您走这条路,可能有助于将值放置在较小的 3x3 JPanel
s 中。
【讨论】:
以上是关于在 Java 中使用 JPanel 的数独板的主要内容,如果未能解决你的问题,请参考以下文章