我如何创建另一个线程,以便我的 ActionListener 在我的星球模拟运行时仍然监听按钮按下事件?
How do I create another thread so that my ActionListener still listens for button press events while my planet simulation runs?
我有一个 JButton 并向其添加了一个 ActionListener
。我还有一个扩展 JPanel
的 SimulationPanel
class 并有一个名为 simulationPanel 的实例。两者都添加到 JFrame
。在 ActionListener
的 actionPerformed 方法中,我调用 simulationPanel.startSimulation()
.
class StartButtonListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent arg0) {
if(mode == MODE_DEFAULT){
mode = MODE_SIMULATING;
startButton.setText("Stop Simulation");
simulationPanel.startSimulation();
} else if(mode == MODE_SIMULATING){
mode = MODE_DEFAULT;
startButton.setText("Start Simulation");
simulationPanel.stopSimulation();
}
}
}
stopSimulation()
方法设置 运行 false。
public void startSimulation() {
running = true;
long time = System.currentTimeMillis();
long time2 = System.currentTimeMillis();
while(running){
time = System.currentTimeMillis();
if(time - time2 > 1000/60){
for(int i = 0; i < planets.size(); i++) {
planets.get(i).setLocation(planets.get(i).getNewLocation());
}
this.paintImmediately(0, 0, 950, 680);
time2 = System.currentTimeMillis();
}
}
}
这会设置我的行星的新位置并每 60 秒重新绘制一次屏幕。
问题在于,一旦进入 while(运行) 循环,ActionListener
不再响应按下的按钮,因此无法调用 stopSimulation()
。我将如何创建一个新线程,使按钮能够在模拟为 运行 时响应?
当然可以,因为您的启动模拟正在阻塞您的主线程。
我建议你使用 Swing Timer http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
Swing是单线程环境,如果你阻塞了Event Dispatching Thread,那么它就无法处理Event Queue,这涉及处理用户输入和获取事件。
当您需要执行较长的 运行ning 任务时,您应该将任务卸载到另一个线程。
请记住,Swing 也不是线程安全的,这意味着您永远不应在事件调度线程的上下文之外创建或修改 UI。
在这方面,您可以使用一些工具让您的生活更轻松。
您可以使用 SwingWorker
,这将允许您在后台 运行 长 running/blocking 任务,但使用 publish
/process
发送更新到 UI 的方法,这不违反 Swing
的单线程规则
您可以使用 Swing Timer
,它允许您安排在 EDT 上下文中执行的定期回调,从而可以安全地从
中更新 UI ]
您可以使用普通的旧 Thread
并使用 SwingUtilities.invokeLater
与 EDT 同步更新,但我不喜欢这种方法,因为通过[传递状态信息很痛苦] =16=]
我有一个 JButton 并向其添加了一个 ActionListener
。我还有一个扩展 JPanel
的 SimulationPanel
class 并有一个名为 simulationPanel 的实例。两者都添加到 JFrame
。在 ActionListener
的 actionPerformed 方法中,我调用 simulationPanel.startSimulation()
.
class StartButtonListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent arg0) {
if(mode == MODE_DEFAULT){
mode = MODE_SIMULATING;
startButton.setText("Stop Simulation");
simulationPanel.startSimulation();
} else if(mode == MODE_SIMULATING){
mode = MODE_DEFAULT;
startButton.setText("Start Simulation");
simulationPanel.stopSimulation();
}
}
}
stopSimulation()
方法设置 运行 false。
public void startSimulation() {
running = true;
long time = System.currentTimeMillis();
long time2 = System.currentTimeMillis();
while(running){
time = System.currentTimeMillis();
if(time - time2 > 1000/60){
for(int i = 0; i < planets.size(); i++) {
planets.get(i).setLocation(planets.get(i).getNewLocation());
}
this.paintImmediately(0, 0, 950, 680);
time2 = System.currentTimeMillis();
}
}
}
这会设置我的行星的新位置并每 60 秒重新绘制一次屏幕。
问题在于,一旦进入 while(运行) 循环,ActionListener
不再响应按下的按钮,因此无法调用 stopSimulation()
。我将如何创建一个新线程,使按钮能够在模拟为 运行 时响应?
当然可以,因为您的启动模拟正在阻塞您的主线程。
我建议你使用 Swing Timer http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
Swing是单线程环境,如果你阻塞了Event Dispatching Thread,那么它就无法处理Event Queue,这涉及处理用户输入和获取事件。
当您需要执行较长的 运行ning 任务时,您应该将任务卸载到另一个线程。
请记住,Swing 也不是线程安全的,这意味着您永远不应在事件调度线程的上下文之外创建或修改 UI。
在这方面,您可以使用一些工具让您的生活更轻松。
您可以使用 SwingWorker
,这将允许您在后台 运行 长 running/blocking 任务,但使用 publish
/process
发送更新到 UI 的方法,这不违反 Swing
您可以使用 Swing Timer
,它允许您安排在 EDT 上下文中执行的定期回调,从而可以安全地从
您可以使用普通的旧 Thread
并使用 SwingUtilities.invokeLater
与 EDT 同步更新,但我不喜欢这种方法,因为通过[传递状态信息很痛苦] =16=]