我正在尝试让 JButton 启动一个解决迷宫的动画。我有一个递归的深度优先搜索函数,可以找到路径,但逐渐为其着色(使用 Thread.sleep() )。该代码完美运行。我遇到的问题是让按钮启动此动画。
当我将函数添加到 JButton 的 actionPerformed
中时,解决方案只会在完全完成后才会出现(而不是一步一步)。我了解到这是因为 Action 监听器是单线程或类似的东西,所以我尝试使用 Swing Timer。我只是创建了一个每隔几毫秒重新绘制 JPanel 的事件,认为这会在后台继续重新绘制 JPanel,从而显示查找解决方案的进度。但这给了我一个运行时错误:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException at Game$1.actionPerformed(Game.java:54) at javax.swing.Timer.fireActionPerformed(Timer.java:313) at javax.swing.Timer$DoPostEvent.run(Timer.java:245) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756) at java.awt.EventQueue.access$500(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:709) at java.awt.EventQueue$3.run(EventQueue.java:703) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76) at java.awt.EventQueue.dispatchEvent(EventQueue.java:726) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
关于如何从 JButton 启动动画有什么想法吗?我觉得这应该比我制作的要简单得多,因为动画本身就可以正常工作,而我想要的只是通过单击 JButton 来启动它。我想我应该从零代码开始,并根据要求提供所需的一切。
Game.java
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;
public class Game {
private static MazeGrid scene;
private static JFrame frame;
private static JButton play;
private static JButton solve;
private static JButton exit;
private static Timer timer;
private static boolean s = false;
public static void init() {
scene = new MazeGrid();
frame = new JFrame();
play = new JButton("Play");
solve = new JButton("Solve");
exit = new JButton("Exit");
frame.setResizable(false);
frame.setSize(900, 700);
frame.setLayout(null);
frame.setLocationRelativeTo(null);
scene.setSize(650, 680);
frame.add(scene, BorderLayout.CENTER);
play.setSize(100, 50);
play.setLocation(650, 10);
frame.add(play);
solve.setSize(100, 50);
solve.setLocation(770, 10);
frame.add(solve);
exit.setSize(100, 50);
exit.setLocation(650, 100);
frame.add(exit);
frame.setVisible(true);
}
public static void main(String[] args) throws InterruptedException {
ActionListener action = new ActionListener(){
@Override
public void actionPerformed(ActionEvent ev) {
if (ev.getSource() == timer) {
scene.repaint();
System.out.println("hello");
}
}
};
timer = new Timer(40, action);
timer.setInitialDelay(0);
timer.start();
init();
play.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
frame.setVisible(false);
init();
}
});
solve.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
scene.solve(1, 1, squares, 40);
timer.stop();
}
});
exit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
frame.setVisible(false);
frame.dispose();
}
});
}
}
解决
private void solve(int x, int y, Squares squares, int delay) {
// reached middle
if (x == N / 2 && y == N / 2)
done = true;
if (x == 0 || y == 0 || x == N + 1 || y == N + 1)
return;
if (done || m.Visited(x, y))
return;
m.setVisited(x, y, true);
if (!(x == 1 & y == 1)) {
squares.getSquare(x, y).setPath(new Color(180, 140, 50));
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
System.err.println("Error while sleeping!");
Thread.currentThread().interrupt();
}
repaint();
}
if (!m.Top(x, y))
solve(x, y + 1, squares, delay);
if (!m.Right(x, y))
solve(x + 1, y, squares, delay);
if (!m.Bottom(x, y))
solve(x, y - 1, squares, delay);
if (!m.Left(x, y))
solve(x - 1, y, squares, delay);
if (done)
return;
if (!(x == 1 & y == 1)) {
squares.getSquare(x, y).setPath(new Color(230, 230, 230));
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
System.err.println("Error while sleeping!");
Thread.currentThread().interrupt();
}
repaint();
}
}
最佳答案
在启动 Timer
之前调用 init()
,否则 scene
对象可能为 null
尝试访问它。
init();
ActionListener action = new ActionListener(){
@Override
public void actionPerformed(ActionEvent ev) {
if (ev.getSource() == timer) {
scene.repaint();
System.out.println("hello");
}
}
};
timer = new Timer(40, action);
timer.setInitialDelay(0);
timer.start();
关于java - 创建一个逐步更新 JPanel 的 ActionListener,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40782883/