繁体   English   中英

如何在加载 Swing 构建 GUI 时更新/绘制 JProgressBar

[英]How to update/paint JProgressBar while Swing is loaded building the GUI

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

我有一个未装饰的小型 JDialog,其中包含一个 JLabel 和一个 JProgressBar,我想在初始化期间在特定位置进行更新,但是,因为事件调度头(根据 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()引起的重绘当然会排队,并在我们全部完成后导致重绘,而不是一路更新..

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

使用Runnable#thread还有另一个选项,但是您必须将所有 output 包装到 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中的所有JProgressBars都非常易于访问和配置

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

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

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

也可以看看:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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