2014-11-14 33 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上)它应该告诉你的主线程杀死你的单独线程导致更新停止。

如果您遇到困难,请随时索要更详细的步骤。