Как программно вызвать кнопку, которая запускается как самостоятельная задача? - PullRequest
2 голосов
/ 08 августа 2011

Я реализовал проблему Игры жизни Конвея в Java Swing.Все работает нормально.Как вы можете видеть на скриншоте ниже, всякий раз, когда нажимается кнопка «Tick», игра переходит к следующей форме жизни.Теперь я планирую включить кнопку «Автоигра» рядом с кнопкой «Тик».Цель этого автоигры проста.Когда я нажимаю на нее, автоматическая операция должна продолжаться так, как будто я нажимаю кнопку с галочкой с интервалом в 1 секунду.

enter image description here

Я попробовал это.Но это, кажется, блокирует все другие операции.Как сделать это действие в отдельной теме?Небольшой фрагмент кода поможет мне.

class AutoPlayListener implements ActionListener{
  public void actionPerformed(ActionEvent e) {
    if(e.getSource() == btnAutoPlay){
      while(true){
        Thread.sleep(1000); //InterruptedException try catch hidden
        btnTick.doClick();
      }
    }
  }
}

Ответы [ 3 ]

6 голосов
/ 08 августа 2011

Используйте javax.swing.Timer.Он сможет работать с существующими ActionListener, если вызовы while(true) и Thread.sleep() удалены.

2 голосов
/ 08 августа 2011

Как сказал @Ranman, вы блокируете основной поток пользовательского интерфейса. Я считаю, что SwingUtilities.invokeLater обычно используется для таких вещей.

2 голосов
/ 08 августа 2011

Есть два варианта:

  1. Начать новую тему. Поток будет содержать цикл while и выполнит метод, который обрабатывает массив. На каждой итерации вызывайте repaint() или invalidate() в своем окне, чтобы сообщить, что оно нуждается в перерисовке.
  2. Используйте таймер. Поток GUI будет вызывать вашу процедуру через регулярные промежутки времени.

Тема:

В методе actionPerformed создайте новый поток. и вызвать его метод запуска. Runnable потока должен запустить цикл while (как вы уже сделали), а затем просто выйти.

Таймер:

Создайте объект в вашем классе типа Timer. Используйте тот из java.swing.Timer, если вы используете Swing (есть также java.util.Timer, который не подходит для операций с графическим интерфейсом). Таймер должен иметь ActionListener, который вызывает ваш метод один раз, но таймер имеет частоту повторения 1000 мс.

Советы

  1. чтобы вызвать действие, вы должны поместить его в отдельный метод, а не прямо под обработчик кнопки. Таким образом, вы не вызываете вещи из GUI вне потока GUI.

, например

tickButton.addActionListener(new ActionListener(){
  public void actionPerformed(ActionEvent e){
    doTick();
  }
});
  1. Не менее важен механизм остановки нити! В общем, не используйте while(true) в потоке, так как он будет потерян; придумайте семафор, чтобы прекратить его.

  2. используйте JToggleButton вместо Button?

  3. Синхронизация: Если вы используете потоки, вам понадобится что-то вроде этого, чтобы новые потоки не создавались при каждом нажатии кнопки:

Код

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