【问题标题】:JFrame only shows components at first creationJFrame 仅在首次创建时显示组件
【发布时间】:2014-10-13 05:58:53
【问题描述】:

当我启动我的应用程序时,它会打开一个 JFrame(主窗口)和一个 JFilechooser 以选择一个输入目录,然后对其进行扫描。

scan 方法本身会创建一个新的JFrame,其中包含一个JButton 和一个JProgressBar,并启动一个新的线程来扫描选定的目录。到目前为止,一切正常。

现在我在我的主窗口中更改目录路径,它再次调用扫描方法。这次它创建了另一个JFrame,其中应该包含JProgressBarJButton,但它显示为空(JFrame 标题仍然设置)。

更新: 最小的例子

public class MainWindow
{
    private JFrame      _frame;
    private JTextArea   _textArea;
    private ProgressBar _progress;

    public MainWindow() throws InterruptedException, ExecutionException
    {
        _frame = new JFrame("Main Window");
        _textArea = new JTextArea();

        _frame.add(_textArea);
        _frame.setSize(200, 200);
        _frame.setVisible(true);

        _textArea.setText(doStuffinBackground());

        _progress.dispose();
    }

    private String doStuffinBackground() throws InterruptedException,
            ExecutionException
    {
        setUpProgressBar();
        ScanWorker scanWorker = new ScanWorker();
        scanWorker.execute();

        return scanWorker.get();
    }

    private void setUpProgressBar()
    {
        // Display progress bar
        _progress = new ProgressBar();
    }

    class ProgressBar extends JFrame
    {
        public ProgressBar()
        {
            super();

            JProgressBar progressBar = new JProgressBar();
            progressBar.setIndeterminate(true);
            progressBar.setStringPainted(false);

            add(progressBar);

            setTitle("Progress Window");
            setSize(200, 200);
            toFront();
            setVisible(true);
        }
    }

    class ScanWorker extends SwingWorker<String, Void>
    {
        @Override
        public String doInBackground() throws InterruptedException
        {
            int j = 0;
            for (int i = 0; i < 10; i++)
            {
                Thread.sleep(1000);
                j += 1;
            }
            return String.valueOf(j);
        }
    }

    public static void main(String[] args) throws InvocationTargetException,
            InterruptedException
    {
        SwingUtilities.invokeAndWait(new Runnable()
        {
            public void run()
            {
                // Start the main controller
                try
                {
                    new MainWindow();
                }
                catch (InterruptedException | ExecutionException e) {}
            }
        });
    }
}

【问题讨论】:

  • 1) 见The Use of Multiple JFrames, Good/Bad Practice? 2) 为了尽快获得更好的帮助,请发布MCVE(最小、完整、可验证的示例)。 3) 你的问题是什么?
  • @andrew-thompson 实际上我想使用 JDialog 因为它会更简洁,因为它会更简洁,而不是禁用 Main JFrame 并在之后重新启用它,但从我在网上发现的情况来看,它会使处理复杂化取消按钮,因为侦听器代码需要完全在 JDialog 类本身中(我的老师不太喜欢这样做,因为它违反了 MVC 模式(至少在他看来)

标签: java multithreading swing components swingworker


【解决方案1】:

从您的scan 方法的基本外观来看,当您扫描目录时,您正在阻止事件调度线程,这会阻止它更新 UI。

具体来说,您似乎并不真正了解CallableFutureTask 的实际用途或如何正确使用它们...

调用FutureTask#run 将在当前线程上下文中调用Callablecall 方法。

查看Concurrency in Swing了解更多详情...

与其尝试以这种方式使用FutureTaskCallable,不如考虑使用SwingWorker,它旨在完成此类工作(并在内部使用CallableFutureTask

查看Worker Threads and SwingWorker了解更多详情

现在,在你从我的喉咙里跳下来告诉我“我第一次运行它就可以工作”之前,那是因为你没有正确启动你的 UI。所有 Swing UI 都应该在 Event Dispatching Thread 的上下文中创建和操作。你main 方法是在通常所说的“主线程”中执行的,它与EDT 不同。这基本上是在你第一次调用scan 时设置侥幸情况,你没有在 EDT 的上下文中运行,允许它工作......并在这个过程中打破了 Swing 的单线程规则......

查看Initial Threads了解更多详情...

我还会考虑使用JDialog 代替另一个框架,即使它不是模态的,它也可以为您的应用程序提供更好的范例,因为它实际上应该只有一个主框架。

根据新代码更新

所以,基本上,return scanWorker.get(); 是一个阻塞调用。它会一直等到doInBackground 方法完成,这意味着它阻塞了 EDT,仍然......'

相反,您应该使用SwingWorkerpublishprocess 和/或done 方法

import java.util.ArrayList;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class MainWindow {

    private JFrame _frame;
    private JTextArea _textArea;
    private ProgressBar _progress;

    public MainWindow() {
        _frame = new JFrame("Main Window");
        _textArea = new JTextArea();

        _frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        _frame.add(new JScrollPane(_textArea));
        _frame.setSize(200, 200);;
        _frame.setVisible(true);

        doStuffinBackground();
    }

    private void doStuffinBackground() {
//        _progress = new ProgressBar();
//        ScanWorker scanWorker = new ScanWorker();
//        scanWorker.execute();
//        return scanWorker.get();

        _progress = new ProgressBar();
        ScanWorker worker = new ScanWorker(_textArea, _progress);
        worker.execute();
        _progress.setVisible(true);
    }

    class ProgressBar extends JDialog {

        public ProgressBar() {
            super(_frame, "Scanning", true);

            JProgressBar progressBar = new JProgressBar();
            progressBar.setIndeterminate(true);
            progressBar.setStringPainted(false);

            add(progressBar);

            setTitle("Progress Window");
            pack();
            setLocationRelativeTo(_frame);
        }
    }

    class ScanWorker extends SwingWorker<List<String>, String> {

        private JTextArea textArea;
        private ProgressBar progressBar;

        protected ScanWorker(JTextArea _textArea, ProgressBar _progress) {
            this.textArea = _textArea;
            this.progressBar = _progress;

        }

        @Override
        protected void process(List<String> chunks) {
            for (String value : chunks) {
                textArea.append(value + "\n");
            }
        }

        @Override
        public List<String> doInBackground() throws Exception {
            System.out.println("...");
            int j = 0;
            List<String> results = new ArrayList<>(25);
            for (int i = 0; i < 10; i++) {
                Thread.sleep(1000);
                j += 1;
                System.out.println(j);
                results.add(Integer.toString(j));
                publish(Integer.toString(j));
            }
            return results;
        }

        @Override
        protected void done() {
            progressBar.dispose();
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new MainWindow();
            }
        });
    }
}

【讨论】:

  • 但是 scanWorker.get() 不应该阻止 Progressbar 被添加到 JFrame 中,对吧?后台任务运行时应用程序是否阻塞并不重要(用户必须等待它完成才能做任何事情)
  • ``SwingWorker#get` 是一个阻塞方法,它阻塞当前线程直到doInBackground 方法完成。因为这是从 EDT 内部调用的,所以它会阻止 EDT 并阻止它执行任何更新...
  • 是的,我也这么认为...但首先我会睡一觉(这里是早上 7 点),也将您的答案标记为正确,现在我只需将其应用于我的应用程序,谢谢你没有放弃我;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-18
  • 1970-01-01
  • 2017-12-31
  • 1970-01-01
相关资源
最近更新 更多