JFrame仅在首次创建时显示组件 [英] JFrame only shows components at first creation

查看:89
本文介绍了JFrame仅在首次创建时显示组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

When I start my application it opens a JFrame (the main window) and a JFilechooser to select an input directory, which is then scanned.

scan方法本身会创建一个包含JButtonJProgressBar的新JFrame,并启动一个新线程来扫描所选目录.到目前为止,一切正常.

The scan method itself creates a new JFrame which contains a JButton and a JProgressBar and starts a new Thread which scans the selected Directory. Up until this point everything works fine.

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

Now I change the Directory Path in my Main Window, which calls the scan method again. This time it creates another JFrame which should contain the JProgressBar and the JButton but it shows up empty (The JFrame Title is still set).

更新: 最小的例子

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) {}
            }
        });
    }
}

推荐答案

scan方法的基本外观上,您在扫描目录时阻止了事件分发线程,这阻止了它更新UI.

From the basic looks of your scan method, you are blocking the Event Dispatching Thread, when you scan the directory, which is preventing it from updating the UI.

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

Specifically, you don't seem to truly understand what Callable and FutureTask are actually used for or how to use them properly...

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

Calling FutureTask#run will call the Callable's call method...from within the current thread context.

有关更多详细信息,请参见 Swing中的并发.

Take a look at Concurrency in Swing for more details...

不要尝试以这种方式使用FutureTaskCallable,而应考虑使用SwingWorker,它专门用于执行此类工作(并在内部使用CallableFutureTask)

Instead of trying to use FutureTask and Callable in this manner, consider using a SwingWorker, which is designed to do this kind of work (and uses Callable and FutureTask internally)

工作线程和SwingWorker 中查看更多详情

Have a look at Worker Threads and SwingWorker for more details

现在,在您跳下嗓子并告诉我它在我第一次运行时起作用"之前,这是因为您没有正确启动UI.应该在事件调度线程的上下文中创建和操纵所有Swing UI. main方法在通常称为主线程"的位置执行,该方法与EDT不同.这基本上是在首次调用scan时出现的fl幸情况,即您不在EDT的上下文中运行,从而使其无法工作...并在此过程中违反了Swing的单线程规则...

Now, before you jump down my throat and tell me that "it works the first time I ran it", that's because you're not starting your UI properly. All Swing UI's should be create and manipulated from within the context of the Event Dispatching Thread. You main method is executed in, what is commonly called, the "main thread", which is not the same as the EDT. This is basically setting up fluke situation in where the first time you call scan, you are not running within the context of the EDT, allowing it to work ... and breaking the single thread rules of Swing in the process...

有关更多详细信息,请参见初始线程 ...

Take a look at Initial Threads for more details...

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

I would also consider using a JDialog instead of another frame, even if it's not modal, it makes for a better paradigm for your application, as it really should only have a single main frame.

已根据新代码更新

因此,基本上,return scanWorker.get();是一个阻止调用.它将等待doInBackground方法完成,这意味着它仍阻止了EDT ...'

So, basically, return scanWorker.get(); is a blocking call. It will wait until the doInBackground method completes, which means it's block the EDT, still...'

相反,您应该使用SwingWorker

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();
            }
        });
    }
}

这篇关于JFrame仅在首次创建时显示组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆