5

我有一个 GUI,在它运行的平台上构建/初始化非常繁重..因此我想在它初始化时更新进度..

我有一个未装饰的小型 JDialog,其中包含一个 JLabel 和一个 JProgressBar,我想在初始化期间在特定位置进行更新,但是,因为事件调度 thead(根据 Swing 规则)用于构建/初始化 GUI,所以进度当然是直到 EDT 再次空闲(即初始化完成)才更新。

我已经使用“paintImmediately”重绘了 JProgressBar,但我似乎无法让它对 JLabel 和对话框本身正常工作。有没有什么简单的推荐/经过验证的方法来完成这个?

干杯...

编辑:添加一个我正在尝试做的例子;当然,大大简化了。

private JLabel progressLabel;
private JProgressBar progressBar;

public static int main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            showProgressDialog();
            progressLabel.setText("construct 1");

            constructSomeHeavyGUI();

            progressLabel.setText("construct 2");
            progressBar.setValue(33);

            constructSomeMoreHeavyGUI();

            progressLabel.setText("construct 3");
            progressBar.setValue(67);

            constructEvenMoreHeavyGUI();

            progressLabel.setText("done");
            progressBar.setValue(100);

            hideProgressDialog();

            showHeavyGUI();

        }
    });
}

只要 EDT 很忙,由上面调用progressBar.setValue()/引起的重绘当然会排队,并在我们全部完成后导致重绘,而不是一路更新..progressLabel.setText()

4

4 回答 4

3

我建议通过使用SwingWorker,然后您可以在 EDT 上正确更新 JProgressBar 并且没有任何冻结或在 Swing 中具有 Concurency 的问题

使用还有另一个选项Runnable#thread,但是您必须将所有输出包装到 GUI 中invokeLater();

例如:

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;

public class TestProgressBar {

    private static void createAndShowUI() {
        JFrame frame = new JFrame("TestProgressBar");
        frame.getContentPane().add(new TestPBGui().getMainPanel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                createAndShowUI();
            }
        });
    }

    private TestProgressBar() {
    }
}

class TestPBGui {

    private JPanel mainPanel = new JPanel();

    public TestPBGui() {
        JButton yourAttempt = new JButton("WRONG attempt to show Progress Bar");
        JButton myAttempt = new JButton("BETTER attempt to show Progress Bar");
        yourAttempt.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                yourAttemptActionPerformed();
            }
        });
        myAttempt.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                myAttemptActionPerformed();
            }
        });
        mainPanel.add(yourAttempt);
        mainPanel.add(myAttempt);
    }

    private void yourAttemptActionPerformed() {
        Window thisWin = SwingUtilities.getWindowAncestor(mainPanel);
        JDialog progressDialog = new JDialog(thisWin, "Uploading...");
        JPanel contentPane = new JPanel();
        contentPane.setPreferredSize(new Dimension(300, 100));
        JProgressBar bar = new JProgressBar(0, 100);
        bar.setIndeterminate(true);
        contentPane.add(bar);
        progressDialog.setContentPane(contentPane);
        progressDialog.pack();
        progressDialog.setLocationRelativeTo(null);
        Task task = new Task("Your attempt");
        task.execute();
        progressDialog.setVisible(true);
        while (!task.isDone()) {
        }
        progressDialog.dispose();
    }

    private void myAttemptActionPerformed() {
        Window thisWin = SwingUtilities.getWindowAncestor(mainPanel);
        final JDialog progressDialog = new JDialog(thisWin, "Uploading...");
        JPanel contentPane = new JPanel();
        contentPane.setPreferredSize(new Dimension(300, 100));
        final JProgressBar bar = new JProgressBar(0, 100);
        bar.setIndeterminate(true);
        contentPane.add(bar);
        progressDialog.setContentPane(contentPane);
        progressDialog.pack();
        progressDialog.setLocationRelativeTo(null);
        final Task task = new Task("My attempt");
        task.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getPropertyName().equalsIgnoreCase("progress")) {
                    int progress = task.getProgress();
                    if (progress == 0) {
                        bar.setIndeterminate(true);
                    } else {
                        bar.setIndeterminate(false);
                        bar.setValue(progress);
                        progressDialog.dispose();
                    }
                }
            }
        });
        task.execute();
        progressDialog.setVisible(true);
    }

    public JPanel getMainPanel() {
        return mainPanel;
    }
}

class Task extends SwingWorker<Void, Void> {

    private static final long SLEEP_TIME = 4000;
    private String text;

    public Task(String text) {
        this.text = text;
    }

    @Override
    public Void doInBackground() {
        setProgress(0);
        try {
            Thread.sleep(SLEEP_TIME);// imitate a long-running task
        } catch (InterruptedException e) {
        }
        setProgress(100);
        return null;
    }

    @Override
    public void done() {
        System.out.println(text + " is done");
        Toolkit.getDefaultToolkit().beep();
    }
}

编辑:

1)你展示了另一个问题,为什么你在飞行/运行时创建大量顶级容器,只创建所需数量的容器并通过以下方式重新使用它removeAll()

2)这可能是您需要的, JTable中的所有JProgressBar都非常易于访问和配置

3)这是你的paintImmediately(),这就是为什么不将任何 Progress 绘制到JLabel而是使用的真正原因JProgressBar#setValue(int);

于 2011-08-16T13:33:10.113 回答
2

将长时间运行的代码移动到单独的线程中,并使用 SwingUtilities.invokeAndWait 或 invokeLater 更新 GUI。

于 2011-08-16T13:27:26.070 回答
2

按照@StanislavLSwingUtilities.invokeLater(...)的建议使用,或者使用SwingWorker

也可以看看:

于 2011-08-16T13:31:55.417 回答
2

可能constructSome*HeavyGUI()真的需要足够长的时间才重要,但更有可能的是填写数据模型是问题所在。相反,构建并显示空的 GUI 元素并启动一个或多个SwingWorker实例来编组每个元素的数据。这里这里都有相关的例子。

附录:如果问题是实例化组件,而不是加载数据模型,您可以将调用链接到invokeLater(),如下面的评论中建议的那样。如果您要实例化这么多组件,请考虑享元模式JTable是一个熟悉的例子。

于 2011-08-16T17:23:40.050 回答