2014-11-14 30 views
1

我試圖隨機生成一個字符串並不斷更新JTextArea。我知道程序會在無限循環的runTest()方法中掛起。我試圖循環顯示這個結果,直到用戶點擊一個停止按鈕。有什麼建議?由於使用線程實時更新JTextArea

import javax.swing.JButton; 
    import javax.swing.JFrame; 
    import javax.swing.JLabel; 
    import javax.swing.JTextArea; 
    import javax.swing.SwingUtilities; 
    import java.awt.BorderLayout; 
    import java.awt.Panel; 
    import java.awt.event.ActionEvent; 
    import java.awt.event.ActionListener; 
    import java.util.Random; 

public class MyApplication extends JFrame { 
    private JTextArea textArea; 
    private JButton b; 
    private String toChange = ""; 

    // Called from non-UI thread 
    private void runQueries() { 
     while (true) { 
      runTest(); 
      updateProgress(); 
     } 
    } 

    public void runTest() { 

     while (true) { 

      if (toChange.length() > 10) { 
       toChange = ""; 
      } 
      Random rand = new Random(); 
      toChange += rand.nextInt(10); 
     } 
    } 

    private void updateProgress() { 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       textArea.append(toChange); 
      } 
     }); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       MyApplication app = new MyApplication(); 
       app.setVisible(true); 
      } 
     }); 
    } 

    private MyApplication() { 

     this.setLayout(null); 
     this.setResizable(false); 
     this.setLocation(100, 100); 
     this.setSize(900, 600); 

     final Panel controlPanel = new Panel(); 
     controlPanel.setLayout(new BorderLayout()); 
     controlPanel.setSize(600, 200); 
     controlPanel.setLocation(50, 50); 
     setDefaultCloseOperation(this.EXIT_ON_CLOSE); 

     textArea = new JTextArea("test"); 
     textArea.setSize(100, 100); 
     textArea.setLocation(200, 200); 
     this.add(textArea); 

     JButton b = new JButton("Run query"); 
     b.setSize(100, 75); 
     b.setLocation(100, 50); 
     this.add(b); 

     b.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 
       Thread queryThread = new Thread() { 
        public void run() { 
         runQueries(); 

        } 
       }; 
       queryThread.start(); 

      } 
     }); 

    } 

} 
+0

我想你想在一個新的線程上重複任務。給我一分鐘來獲取代碼。 – Joehot200 2014-11-14 14:47:12

+0

使'Thread queryThread'成爲**類**變量並執行'queryThread。您的「停止」按鈕的「actionPerformed()」方法中的interrupt()'。有關更多信息,請參閱[Java Thread Primitive Deprecation](https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html)。 – 2014-11-14 14:52:59

+2

Java GUI必須適用於不同的操作系統,屏幕大小,屏幕分辨率等。因此,它們不利於像素的完美佈局。請使用佈局管理器或[它們的組合](http://stackoverflow.com/a/5630271/418556)以及[white space]的佈局填充和邊框(http://stackoverflow.com/a/17874718/ 418556)。 – 2014-11-14 14:56:31

回答

3
int initialDelay = 1000; // start after 1 seconds 
    int period = 1000;  // repeat every 1 seconds 
    Timer timer = new Timer(); 
    TimerTask task = new TimerTask() { 
     public void run() { 
     if (toChange.length() > 10) { 
      toChange = ""; 
     } 
     Random rand = new Random(); 
     toChange += rand.nextInt(10); 
    }; 
    timer.scheduleAtFixedRate(task, initialDelay, period); 

你會想要的東西,就像上面我只是做了。否則,你只是簡單地凍結你的線程,因此,永遠不會調用updateProgress();

在那個筆記上,爲什麼你甚至需要在runTest()方法中使用while (true)循環?它始終在runQueries()中通過while循環正在運行和更新,這意味着while (true)循環看起來沒有任何意義。

澄清: 我在說你應該把你的runTest()和updateProgress()放在一個延遲的任務中,而不是一個真正的循環 - 否則,你只是通過把它們放在相同的位置線程。

然而,改變這也應該解決這個問題:

b.addActionListener(new ActionListener() { 
     public void actionPerformed(ActionEvent e) { 
      int initialDelay = 100; // start after 0.1 seconds 
      int period = 100;  // repeat every 0.1 seconds 
     { 
      Timer timer = new Timer(); 
      TimerTask task = new TimerTask() { 
      public void run() { 
       runTest(); 
      }; 
     timer.scheduleAtFixedRate(task, initialDelay, period); 
     } 
     { 
      Timer timer = new Timer(); 
      TimerTask task = new TimerTask() { 
      public void run() { 
       updateProgress(); 
      }; 
     timer.scheduleAtFixedRate(task, initialDelay, period); 
     } 
    } 
    }); 

} 

和的runTest()..

public void runTest() { 
     if (toChange.length() > 10) { 
      toChange = ""; 
     } 
     Random rand = new Random(); 
     toChange += rand.nextInt(10); 
} 

和的UpdateProgress()..

private void updateProgress() { 
    textArea.append(toChange); 
} 

注意,我在SO編輯器中鍵入代碼,而不是在IDE中。對不起,任何語法錯誤。

+0

我正在努力理解這一點,你能否詳細說明我使用的例子?計時器應該在哪裏創建? 謝謝 – 2014-11-14 15:20:03

+0

@JavaNoob我添加了一些澄清,加上我以前沒有意識到的解決方案。 – Joehot200 2014-11-14 15:28:19

+0

啊,解決方案顯示一次然後凍結程序。 – 2014-11-14 15:34:13

0

試試這個。

private void runQueries() { 
    while (!stop) { 
     runTest(); 
     updateProgress(); 
    } 
} 

使當你想比較,例如命中停止按鈕stop是真實的。然後

public void runTest() { 

    while (true) { 

     if (toChange.length() > 10) { 
      toChange = ""; 
      break; 
     } 
     Random rand = new Random(); 
     toChange += rand.nextInt(10); 
    } 
} 

,如果你不打破循環,你的updateProgress不會叫..

1

其中之一,我不能強調不夠的是,在Java中所有GUI元素應在EventDispatchThread運行的最重要的事情如果不這樣做會導致不可預知的錯誤行爲。

閱讀了關於在這裏:​​https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

從我可以通過你是在一個恆定循環凍結它livelocking您的應用程序當前的代碼告訴。

你想要做的是創建一個單獨的線程,創建後不斷更新EDT中的TextArea,但保持此線程在主應用程序中引用。 一旦用戶按下按鈕(這將在EDT上)它應該告訴你的主線程殺死你的單獨線程導致更新停止。

如果您遇到困難,請隨時索要更詳細的步驟。