2

我正在为一个项目制作一个国际象棋程序。我正在尝试在棋盘的一侧添加一个移动历史记录框。移动历史运行正常,数据已正确发送到文本区域,但 JTextArea 内的文本在 AI 考虑他的移动时消失了。

public void aiMove(){
  if (!playing){ return; }
  paintImmediately(0,0,totalX,totalY);
  ai = eve.getMove(chess,wtm,aiOut);   //text disappears here
  chess.makeMove(ai);
  wtm = !wtm;
  humanMove = true;
  writeMove(ai);                   //updates move history, text reappears here
  playing = stillPlaying();
  repaint();
}

private void writeMove(Move move){
  char c = "abcdefgh".charAt(7-move.fromY);
  char h ="abcdefgh".charAt(7-move.toY);
  String s = Character.toString(c)+(move.fromX+1)+" - "+Character.toString(h)+(move.toX+1)+"  ";
  if (!wtm){
    String q = chess.getFullMove()+".  "+s+"  ";
    moves.setText(moves.getText()+q);
  }
  else {
    moves.setText(moves.getText()+s+"\n");
  }
}

这是正在发生的事情的打印屏幕。 http://s13.postimage.org/mh7hltfk7/JText_Area_disappear.png


已解决 感谢所有回复。我改变了 aiMove() 所以它创建了一个线程。这就是我所做的。

尝试#3 ...摇摆对我来说仍然很陌生。我不想将 writeMove 更改为 getMove,否则我将不得不稍微重写人类的轮到。由于该项目基本上已经完成,我试图避免尽可能多的工作 :) 无论如何,GUI 完全是可选的,我只是为了好玩,并尝试学习一些摇摆。

public void aiMove(){
  if (!playing){ return; }
  if (!aiThread.isAlive()){
    aiThread = new Thread(){
      public void run(){
        ai = eve.getMove(chess,wtm,aiOut);
        chess.makeMove(ai);
        wtm = !wtm;
        humanMove = true;
        SwingUtilities.invokeLater(new Runnable(){
          public void run(){
            writeMove(ai);
          }
        });
        repaint();
        playing = stillPlaying();
      }
    };
    aiThread.start();
  }
}

它还解决了我之前遇到的一个问题,如果我按住“a”键(强制 ai 移动),它会排队许多强制 ai 移动。现在这不会发生。

4

1 回答 1

6

问题是您的 AI 思维是 CPU 密集型/耗时的,因此它被认为是一项长期运行的任务。您不应在 GUI 事件调度线程上执行长时间运行的任务,因为这会导致 UI 看起来冻结,因此仅在任务完成后才显示更新。

幸运的是,您可以使用 2 种不同的方法:

SwingWorker 子类可以定义一个方法 done,当后台任务完成时,该方法会在事件调度线程上自动调用。

SwingWorker 实现 java.util.concurrent.Future。该接口允许后台任务向其他线程提供返回值。此接口中的其他方法允许取消后台任务并发现后台任务是否已完成或已取消。

后台任务可以通过调用 SwingWorker.publish 来提供中间结果,从而从事件调度线程调用 SwingWorker.process。

后台任务可以定义绑定属性。对这些属性的更改会触发事件,从而在事件调度线程上调用事件处理方法。

  • Thread或者,为 AI 思维创建单独的并包含setText调用SwingUtilities.invokeLater(...);

    Thread t=new Thread(new Runnable() {
         @Override
         public void run() {
        }
    });
    t.start();
    

更新

阅读 MadProgrammers 评论(+1)后,请记住通过SwingUtilities.invokeLater(..)块在 EDT 上创建/操作您的 GUI/Swing 组件。你可以在这里阅读更多。

更新 2:

那个编辑违背了这一点,在块中对 EDT 的唯一调用SwingUtilitites应该是setText或至少唯一的代码来操作 Swing 组件,即

public void aiMove(){
  if (!playing){ return; } 
  if (!aiThread.isAlive()){  //originally initialized by constructor
    aiThread = new Thread(){
      public void run(){
            ai = eve.getMove(chess,wtm,aiOut);
            chess.makeMove(ai);
            wtm = !wtm;
            humanMove = true;
        SwingUtilities.invokeLater(new Runnable(){
          public void run(){
            writeMove(ai);
          }
        });
        repaint();
        playing = stillPlaying();
      }
    };
    aiThread.start();
  }
}
于 2012-12-22T19:59:55.483 回答