[英]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。
有可能constructSome*HeavyGUI()
確實需要足夠長的時間才有意義,但更有可能的是填充數據模型是問題所在。 相反,構建並顯示空的 GUI 元素並啟動一個或多個SwingWorker
實例來編組每個元素的數據。 這里和這里都有相關的例子。
附錄:如果問題是實例化組件,而不是加載數據模型,您可以將調用鏈接到invokeLater()
,如下面的評論中建議的那樣。 如果您要實例化這么多組件,請考慮享元模式。 JTable
就是一個熟悉的例子。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.