2012-12-22 85 views
2

我正在製作一個項目的國際象棋程序。我正在嘗試將移動歷史記錄框添加到董事會一方。移動歷史記錄工作正常,數據正確發送到文本區域,但JTextArea內的文本消失,而AI正在考慮他的移動。JTextArea文字消失

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力移動),它會排隊迫使許多人工智能移動。現在沒有發生。

+0

運行時間setText或至少只代碼在單獨的線程中執行耗時的任務將避免GUI凍結。 –

+0

我不確定它是否發生了什麼,但是您應該只使用主線程進行GUI操作,因爲GUI將被此線程刷新。業務邏輯(特別是如果它可能需要一段時間才能完成)應該在一個單獨的線程中。 – SJuan76

+1

您更新的代碼違反了線程規則Swing - *您不應該從除事件分派線程以外的任何線程更新任何UI元素* - 檢查David的答案,因爲他提供了重要的指針,以解決這些問題 – MadProgrammer

回答

6

問題是您的AI思維是CPU密集型/耗時的,因此它被認爲是長時間運行的任務。您不應該在GUI Event Dispatch Thread上執行長時間運行的任務,因爲這會導致UI看起來凍結,因此只會在任務完成後才顯示更新。

幸運的是有2種不同的方法,你可以使用:

的SwingWorker的子類可以定義一個方法,完成,這是 當任務完成時,在事件調度線程上自動調用。

SwingWorker實現java.util.concurrent.Future。 這個接口允許後臺任務提供一個返回值給另一個線程的 。此接口中的其他方法允許取消後臺任務的 ,並發現後臺任務是否已完成 或已取消。

後臺任務可以通過調用SwingWorker.publish,造成 SwingWorker.process從事件分派線程調用​​提供 中間結果。

後臺任務可以定義綁定屬性。對這些 屬性的更改觸發事件,導致事件處理方法在事件分派線程上調用 。

  • 或者對AI的思考創建單獨的Thread和包裝setText呼叫SwingUtilities.invokeLater(...);

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

UPDATE

閱讀MadProgrammers評論(+1吧)後,請注意記住要創建/操作你的GUI /通過SwingUtilities.invokeLater(..)塊在EDT上旋轉組件。你可以閱讀更多關於它here

更新2:

即編輯被擊敗點,在SwingUtilitites塊上EDT唯一呼叫應當操縱一個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(); 
    } 
} 
+0

@twentylemon請參閱更新2迴應你的編輯 –

+0

@twentylemon對不起getMove更新真的不需要。因此我編輯了它。我看到你更新了你的代碼,所以它仍然會出現問題?確保你在edt上創建了你的jframe和其他組件,這通常可以通過將調用包裝到SwingUtilities.invokeXXX塊中創建jframe來解決。如果仍然不起作用發佈SSCCE –

+0

不,問題已解決。我爲其他與我有同樣問題的人更新了它。感謝你的幫助。 – twentylemon