How to programatically call a button that runs as an independent task?
I have implemented Conway's Game o开发者_StackOverflow中文版f Life problem in Java swing. Everything is working fine. As you can see in the screenshot below, whenever the "Tick" button is clicked, the game progresses to the next life form. Now, I am planning to include an "Autoplay" button alongside "Tick" button. The purpose of this autoplay is simple. When I hit it, an automated operation should carry on as if I am pressing tick button at an interval of 1 second.
I tried this. But this seems to block all the other operations. How to do this action in a separate thread? A small code snippet would get me going.
class AutoPlayListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btnAutoPlay){
while(true){
Thread.sleep(1000); //InterruptedException try catch hidden
btnTick.doClick();
}
}
}
}
Use a javax.swing.Timer
. It will be able to work with the existing ActionListener
if the while(true)
and Thread.sleep()
calls are removed.
As @Ranman said you're blocking main UI thread. I believe SwingUtilities.invokeLater is usually used for things like this.
There are two options:
- Start a new thread. The thread will contain the while loop, and execute a method that processes the array. In each iteration, call
repaint()
orinvalidate()
on your window to tell it that it needs redrawing. - Use a Timer. The GUI thread will call your routine at regular intervals.
Threads:
In actionPerformed
method, create a new Thread. and call its start method.
The Runnable
of the thread should run a while loop (as you have already done), and then simply exit.
Timer:
Create an object in your class of type Timer. Use the one in java.swing.Timer
if you are using swing (there is also java.util.Timer
which isn't good for GUI ops). The timer should have an ActionListener
that calls your method once, but the Timer has a repeat rate of 1000ms.
Tips
- to invoke the action, you should put it in a separate method, rather than directly under the button handler. That way, you aren't calling GUI stuff from outside the GUI thread.
e.g.
tickButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
doTick();
}
});
The mechanism to stop the thread is equally important! In general, don't use a
while(true)
in a thread as it will get lost; invent a semaphore to terminate it.use a
JToggleButton
rather thanButton
?Synchronization: If you use threads, you will need something like this, to prevent new threads being created each time the button is pressed:
Code
Thread autoplayThread = null;
Object lock;
boolean autoplaying = false;
public void actionPerformed(ActionEvent e){
synchronized(lock){ // prevent any race condition here
if(!autoplaying && autoplayThread==null ){
autoplaying = true;
autoplayThread = new Thread(new Runnable(){
public void run(){
try{
while(autoplaying){ .... }
}finally{
synchronized(lock) {
autoplaying=false;
autoplayThread=null;
}
}
}
});
autoplayThread.start();
}else{ // stop the thread!
autoplaying=false;
}
}
}
精彩评论