簡體   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