Java - 全局,可重用的加载对话框

时间:2013-01-01 19:50:25

标签: java swing dialog loading global

我正在尝试实现一个全局加载对话框...我想调用一些静态函数来显示对话框和一些静态函数来关闭它。与此同时,我正在主线程或子线程中做一些工作......

我尝试过关注,但对话框没有更新...只有一次,在再次隐藏之前,它会更新......

    private static Runnable getLoadingRunable()
{
    if (loadingRunnable != null)
    {
        loadingFrame.setLocationRelativeTo(null);
        loadingFrame.setVisible(true);  
        return loadingRunnable;
    }

    loadingFrame = new JFrame("Updating...");
    final JProgressBar progressBar = new JProgressBar();
    progressBar.setIndeterminate(true);
    final JPanel contentPane = new JPanel();
    contentPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
    contentPane.setLayout(new BorderLayout());
    contentPane.add(new JLabel("Updating..."), BorderLayout.NORTH);
    contentPane.add(progressBar, BorderLayout.CENTER);
    loadingFrame.setContentPane(contentPane);
    loadingFrame.pack();
    loadingFrame.setLocationRelativeTo(null);
    loadingFrame.setVisible(true);  

    loadingRunnable = new Runnable() {
        public void run() {

            try {
                while (running) {
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    loadingFrame.setVisible(false);
                }
            });
        }
    };
    return loadingRunnable;
}

public static void showLoadingBar() {
    System.out.println("showLoadingBar");
    running = true;

    threadLoadingBar = new Thread(getLoadingRunable());
    threadLoadingBar.start();
}

public static void hideLoadingBar() {
    System.out.println("hideLoadingBar");
    running = false;
    threadLoadingBar = null;
}

2 个答案:

答案 0 :(得分:7)

Swing使用单线程模型进行事件调度(包括绘制更新),因此,您永远不应该在事件调度线程(EDT)中执行任何长时间运行或阻塞操作。

Swing也不是线程安全的,这意味着你永远不应该在EDT之外创建或修改任何UI组件。

全球进展对话框的基本概念是提供一个不依赖于需要执行的工作的框架,这将责任区域分开,负责显示进度的进度对话框和工人/引擎负责做实际的工作。

最简单的解决方案是使用SwingWorker。这提供了在单独的线程,状态更改和进度通知中执行长时间运行任务的机制。

enter image description here

public class TestProgressDialog {

    public static void main(String[] args) {
        new TestProgressDialog();
    }

    public TestProgressDialog() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                SwingWorker worker = new SwingWorker() {
                    @Override
                    protected Object doInBackground() throws Exception {
                        for (int index = 0; index < 100; index++) {
                            Thread.sleep(50);
                            setProgress(index);
                        }
                        return null;
                    }

                };

                ProgressDialog.showProgress(null, worker);

                System.exit(0);

            }

        });
    }

    public static class ProgressDialog extends JDialog {

        private JLabel message;
        private JLabel subMessage;
        private JProgressBar progressBar;

        public ProgressDialog(Component parent, SwingWorker worker) {

            super(parent == null ? null : SwingUtilities.getWindowAncestor(parent));
            setModal(true);

            ((JComponent)getContentPane()).setBorder(new EmptyBorder(8, 8, 8, 8));

            message = new JLabel("Doing important stuff");
            subMessage = new JLabel("Go make you're self a cup of coffee...");
            progressBar = new JProgressBar();

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = new Insets(2, 2, 2, 2);
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.WEST;
            add(message, gbc);

            gbc.gridy++;
            add(subMessage, gbc);

            gbc.gridy++;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            add(progressBar, gbc);

            pack();

            worker.addPropertyChangeListener(new PropertyChangeHandler());
            switch (worker.getState()) {
                case PENDING:
                    worker.execute();
                    break;
            }

        }

        public static void showProgress(Component parent, SwingWorker worker) {

            ProgressDialog dialog = new ProgressDialog(parent, worker);
            dialog.setLocationRelativeTo(parent);
            dialog.setVisible(true);

        }

        public class PropertyChangeHandler implements PropertyChangeListener {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getPropertyName().equals("state")) {
                    SwingWorker.StateValue state = (SwingWorker.StateValue) evt.getNewValue();
                    switch (state) {
                        case DONE:
                            dispose();
                            break;
                    }
                } else if (evt.getPropertyName().equals("progress")) {
                    progressBar.setValue((int)evt.getNewValue());
                }
            }

        }

    }

}

答案 1 :(得分:5)

如果它没有动画,则表示您在显示加载框架时正在事件派发线程中工作。这个后台工作应该在另一个线程中完成。

这是一个不起作用的例子:

public static void main(String[] args) throws Exception {
    SwingUtilities.invokeLater(
        new Runnable() {
            @Override
            public void run() {
                try {
                    showLoadingBar();
                    Thread.sleep(10000L); // doing work in the EDT. Prevents the frame from animating
                    hideLoadingBar();
                }
                catch (InterruptedException e) {
                }
            }
        }
    );
}

这是一个有效的例子:

public static void main(String[] args) throws Exception {
    showLoadingBar();
    Thread.sleep(10000L); // doing work outside of the EDT. Everything works fine
    hideLoadingBar();
}

旁注:实例化,填充并使加载框可见的代码应该包含在SwingUtilities.invokeLater()中,因为它必须在EDT中运行。