简体   繁体   中英

JFrame only shows components at first creation

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

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.

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).

update: minimal example

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

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.

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

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

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

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)

Have a look at Worker Threads and SwingWorker for more details

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...

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.

Updated based on new code

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...'

Instead, you should be making use of the publish , process and/or done methods of the 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();
            }
        });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM