【问题标题】:Output to jTextArea in realtime实时输出到 jTextArea
【发布时间】:2011-02-12 05:37:19
【问题描述】:

我有一些代码需要几分钟来处理,它必须为一个长数组中的每个字符串连接到网络,每个字符串都是一个 url。我想让它每次连接时都应该刷新 jtextarea,这样用户就不会盯着看起来冻结 20 分钟的空白页面。或者需要多长时间。这是我尝试过但没有成功的示例:

try {
            ArrayList<String> myLinks = LinkParser.getmyLinksArray(jTextArea1.getText());
            for (String s : myLinks) {
                jTextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
            }
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(jTextArea1, "Parsing Error", "Parsing Error", JOptionPane.ERROR_MESSAGE);
            Logger.getLogger(MYView.class.getName()).log(Level.SEVERE, null, ex);
        }

【问题讨论】:

    标签: java user-interface refresh real-time jtextarea


    【解决方案1】:

    swing/awt 是一个单线程库,所以一旦显示了一个组件,仅仅改变它的外观就不能正常工作。您需要更改 GUI 线程上的组​​件,而不是您的线程。为此,请使用 SwingUtilities.invokeLater... 包装任何更新组件的代码,如

    SwingUtilities.invokeLater(new Runnable()
    {
        public void run()
        {
            jTextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
        }
    });
    

    您还想限制您在 gui 线程上执行的操作以避免 gui 变得迟缓,因此如果 checkFileStatus 很耗时,请在 run 方法之外执行它并将结果存储在最终的局部变量中,然后访问run() 代码中的变量。

    【讨论】:

    • SwingUtils 在什么包中?
    • 对不起 javax.swing.SwingUtilities
    【解决方案2】:

    问题是您需要异步执行计算。您应该创建一个执行计算的后台线程,然后使用SwingUtilities.invokeLater 更新 JTextArea。

    final ArrayList myLinks = //... (新线程() { 公共无效运行(){ 对于(字符串 s:myLinks){ 尝试{ 最终字符串结果 = LinkChecker.checkFileStatus(s) + "\n"; SwingUtilities.invokeLater(new Runnable(){ 公共无效运行(){ jtextArea2.append(结果); } }); }catch(IOException 错误){ // 处理错误 } } } })。开始();

    编辑
    有人指出,JTextArea 的 append 函数实际上是线程安全的(与大多数 Swing 函数不同)。因此,对于这种特殊情况,无需通过 invokeLater 对其进行更新。但是,您仍然应该在后台线程中进行处理以允许 GUI 更新,所以代码是:

    final ArrayList myLinks = //... (新线程() { 公共无效运行(){ 对于(字符串 s:myLinks){ 尝试{ jtextArea2.append(LinkChecker.checkFileStatus(s) + "\n"); }catch(IOException 错误){ // 处理错误 } } } })。开始();

    但是,对于修改 Swing 对象的几乎所有其他操作,您将需要使用 invokeLater(以确保修改发生在 GUI 线程中),因为几乎所有 Swing 函数都不是线程安全的。

    【讨论】:

    • 谢谢,我已经准备好了,但它告诉我 s 必须是最终的,所以它应该是 for (final String s : mylinks) 吗?
    • 对于上面的例子,myLinks 需要是最终的。
    • 但是如果你在线程之外有for循环,那么你需要将s设为final(创建final fs = s;并使用fs)。
    • append(...) 方法是线程安全的(根据该方法的 API 文档),因此在此示例中您不需要使用 invokeLater。
    • +1 指出 append() 是 EDT 安全的 - 大多数人忘记查看 API 文档。
    【解决方案3】:

    您需要研究 Swing 中的线程及其relationship to GUI updates。在 Swing 中影响或使用 GUI 组件的任何事情都必须在称为 Event Dispatch Thread (EDT) 的特殊线程上完成。

    如果您的代码 sn-p,如果它冻结了 GUI,我想它正在 EDT 中运行。在 EDT 上执行长时间运行的操作会使 GUI 无响应,因为在长时间运行的进程正在使用线程时无法进行进一步的更新。

    有一个名为SwingWorker 的帮助程序类,它允许您将长时间运行的计算卸载到后台线程,然后在完成后对GUI 线程进行更新。 SwingWorker 负责 GUI 线程和后台线程之间的上下文切换。您还可以显示进度条,让用户知道长时间运行的进程的状态,以便他们知道您的应用程序没有挂起。

    【讨论】:

    • 谢谢。上面的答案成功了,所以我现在可以让它工作,但我认为从长远来看,我应该研究这个并了解它是如何工作的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-17
    • 1970-01-01
    • 2023-03-24
    相关资源
    最近更新 更多