我编码了一个Swing类,其中主要只有一个对方法solve()
的调用。这个方法实现了一个while循环,并在几乎每一轮中使用另一个方法,这个方法被称为paint(),并且只更改9x9GridLayout中一个JPanel的值,该JPanel引用存储在2D数组中。
我需要一个paint()
的定时器,所以在修改面板之前,2秒过去。第一次调用时它会这样做,但在2秒之后,它会不等待地完成每一轮循环。目标是在修改任何面板之前观察2秒的时间。我尝试使用javax Timer。Thread.sleep()显然在Swing类中不起作用。
import javax.swing.*;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.Stack;
public class SudokuApp {
private JFrame frame;
int i = 9;
int j = 9;
JPanel[][] board = new JPanel[i][j];
JButton nextButton = new JButton("Resolver");
Box[][] sudoku = new Box[9][9];
Box newCurrent = new Box();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SudokuApp window = new SudokuApp();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SudokuApp() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setTitle("Sudoku Solver");
frame.setBounds(100, 100, 510, 555);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
panel.setLayout(new GridLayout(i, j));
//Initiate board
for (int m = 0; m < i; m++) {
for (int n = 0; n < j; n++) {
board[m][n] = new JPanel();
JTextField textField = new JTextField(1);
textField.setFont(new Font("Lucida Grande", Font.BOLD, 25));
board[m][n].add(textField);
panel.add(board[m][n]);
}
}
Box last = new Box();
for (int m = 0; m < this.i; m++) {
for (int n = 0; n < this.j; n++) {
sudoku[m][n] = new Box(m, n);
if (m != 0 || n != 0) sudoku[m][n].back = last;
last = sudoku[m][n];
}
}
//Solve button action listener
nextButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {
if (validBoard()) {
nextButton.setEnabled(false);
solve();
}
else JOptionPane.showMessageDialog(null, "Condiciones iniciales inválidas.");
} });
frame.getContentPane().add(nextButton, BorderLayout.SOUTH);
}
private boolean validBoard() {
for (int m = 0; m < i; m++) {
for (int n = 0; n < j; n++) {
JTextField current = (JTextField) board[m][n].getComponent(0);
String text = current.getText();
if (text.length() == 1 && !text.contains("0")) {
try {
int number = Integer.parseInt(text);
if (!add(number, m, n)) return false;
current.setEditable(false);
} catch (NumberFormatException e) {
return false;
}
} else {
current.setText("");
current.setEditable(false);
}
}
}
return true;
}
private void solve() {
int i = 0;
int j = 0;
int newI = i + 1;
while (i < 9) {
while (j < 9) {
Box current = sudoku[i][j];
if (current.number == 0) {
HashSet<Integer> possibles = new HashSet<>();
HashSet<Integer> takenNumbers = getTakenNumbers(i, j);
for (int k = 1; k <= 9; k++) if (!takenNumbers.contains(k)) possibles.add(k);
if (possibles.isEmpty()) {
current.number = 0;
erase(i, j);
tryOther(current.back);
i = newCurrent.i;
newI = i + 1;
j = newCurrent.j + 1;
} else {
for (Integer p : possibles) current.possibles.push(p);
current.number = (int) current.possibles.pop();
paint(current.number, i, j);
j++;
newI = i + 1;
}
} else {
j++;
newI = i + 1;
}
}
i = newI;
j = 0;
}
}
private void tryOther(Box box) {
if (box.possibles.empty()) {
if (box.back != null) {
if (!box.initial) {
box.number = 0;
erase(box.i, box.j);
}
tryOther(box.back);
}
else newCurrent = null;
} else {
if (getTakenNumbers(box.i, box.j).contains(box.possibles.peek())) {
box.possibles.pop();
tryOther(box);
}
newCurrent = box;
box.number = (int) box.possibles.pop();
paint(box.number, box.i, box.j);
}
}
private boolean add(int a, int i, int j) {
if (getTakenNumbers(i, j).contains(a)) return false;
sudoku[i][j].number = a;
sudoku[i][j].initial = true;
return true;
}
private void paint(int a, int i, int j) {
JPanel panel = board[i][j];
JTextField jTextField = (JTextField) board[i][j].getComponent(0);
jTextField.setFont(new Font("Lucida Grande", Font.PLAIN, 25));
jTextField.setForeground(Color.gray);
ActionListener task = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("T");
}
};
Timer timer = new Timer(3000 , task);
timer.setRepeats(false);
timer.start();
jTextField.setText("" + a);
panel.revalidate();
panel.repaint();
}
private void erase(int i, int j) {
JTextField jTextField = (JTextField) board[i][j].getComponent(0);
jTextField.setText("");
board[i][j].revalidate();
board[i][j].repaint();
}
private HashSet<Integer> getTakenNumbers(int i, int j) {
HashSet<Integer> takenNumbers = new HashSet<>();
for (int k = 0; k < 9; k++) if (sudoku[i][k].number != 0 && k != j) takenNumbers.add(sudoku[i][k].number);
for (int k = 0; k < 9; k++) if (sudoku[k][j].number != 0 && k != i) takenNumbers.add(sudoku[k][j].number);
int bigBoxRow = 0;
int bigBoxColumn = 0;
if (i <= 5 && i > 2) bigBoxRow = 3;
if (i <= 8 && i > 5) bigBoxRow = 6;
if (j <= 5 && j > 2) bigBoxColumn = 3;
if (j <= 8 && j > 5) bigBoxColumn = 6;
for (int k = bigBoxRow; k < bigBoxRow + 3; k++) for (int l = bigBoxColumn; l < bigBoxColumn + 3; l++) if (sudoku[k][l].number != 0 && k != i && l != j) takenNumbers.add(sudoku[k][l].number);
return takenNumbers;
}
}
import java.util.Stack;
public class Box {
public int number = 0;
public int i, j;
public Stack possibles = new Stack();
public Box back;
public boolean initial = false;
public Box(int i, int j) {
this.i = i;
this.j = j;
}
public Box() {}
}
您需要更改程序的结构。具体来说,您将希望去掉求解方法中嵌套的while循环,而代之以单个Swing Timer。该Timer将重复9*9次,其中将有一个计数器字段,您将希望在其中执行当前在嵌套while循环中执行的代码。
它可能看起来像。。。
// TIMER_DELAY could be 2000 if you want a 2 second delay
new Timer(TIMER_DELAY, new ActionListener() {
private int i = 0;
private int j = 0;
@Override
public void actionPerformed(ActionEvent e) {
// MAX_ROWS is 9
if (i == MAX_ROWS) {
// we've iterated through the whole thing
((Timer) e.getSource()).stop();
}
if (j == MAX_ROWS) {
// we've gone through all the columns
i++; // the next row
j = 0; // start at column 0
}
// TODO the code in the nested while loop.
j++;
}
}).start();
例如,一个功能正常的程序将文本延迟放置在字段中(但不能解决任何问题):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SudokuMCVE extends JPanel {
private static final int CLUSTER = 3;
private static final int MAX_ROWS = 9;
private static final float FIELD_PTS = 32f;
private static final int GAP = 3;
private static final Color BG = Color.BLACK;
private static final Color SOLVED_BG = Color.LIGHT_GRAY;
public static final int TIMER_DELAY = 2 * 1000;
private JTextField[][] fieldGrid = new JTextField[MAX_ROWS][MAX_ROWS];
public SudokuMCVE() {
JPanel mainPanel = new JPanel(new GridLayout(CLUSTER, CLUSTER));
mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
mainPanel.setBackground(BG);
JPanel[][] panels = new JPanel[CLUSTER][CLUSTER];
for (int i = 0; i < panels.length; i++) {
for (int j = 0; j < panels[i].length; j++) {
panels[i][j] = new JPanel(new GridLayout(CLUSTER, CLUSTER, 1, 1));
panels[i][j].setBackground(BG);
panels[i][j].setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
mainPanel.add(panels[i][j]);
}
}
for (int row = 0; row < fieldGrid.length; row++) {
for (int col = 0; col < fieldGrid[row].length; col++) {
fieldGrid[row][col] = createField(row, col);
int i = row / 3;
int j = col / 3;
panels[i][j].add(fieldGrid[row][col]);
}
}
setLayout(new BorderLayout());
add(mainPanel, BorderLayout.CENTER);
add(new JButton(new SolveAction("Solve")), BorderLayout.PAGE_END);
}
private JTextField createField(int row, int col) {
JTextField field = new JTextField(2);
field.setHorizontalAlignment(JTextField.CENTER);
field.setFont(field.getFont().deriveFont(Font.BOLD, FIELD_PTS));
return field;
}
private class SolveAction extends AbstractAction {
public SolveAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
@Override
public void actionPerformed(ActionEvent e) {
new Timer(TIMER_DELAY, new ActionListener() {
private int i = 0;
private int j = 0;
@Override
public void actionPerformed(ActionEvent e) {
// MAX_ROWS is 9
if (i == MAX_ROWS) {
((Timer) e.getSource()).stop();
}
if (j == MAX_ROWS) {
i++;
j = 0;
}
int number = (int) (MAX_ROWS * Math.random()) + 1;
fieldGrid[i][j].setBackground(SOLVED_BG);
fieldGrid[i][j].setText(String.valueOf(number));
j++;
}
}).start();
}
}
private static void createAndShowGui() {
SudokuMCVE mainPanel = new SudokuMCVE();
JFrame frame = new JFrame("SudokuMCVE");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}