开发者

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.

How to programatically call a button that runs as an independent task?

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:

  1. Start a new thread. The thread will contain the while loop, and execute a method that processes the array. In each iteration, call repaint() or invalidate() on your window to tell it that it needs redrawing.
  2. 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

  1. 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();
  }
});
  1. 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.

  2. use a JToggleButton rather than Button?

  3. 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;
    }
  }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜