2011-08-16 51 views
5

我有一個GUI,它在它運行的平臺上構建/初始化時相當繁重。因此,我想在初始化時更新進度。如何在Swing加載時更新/繪製JProgressBar構建GUI

我有一個小的未裝飾的JDialog,它包含一個JLabel和一個JProgressBar,我想在初始化期間的特定位置進行更新,但是,因爲事件調度thead(按照Swing規則)用於構建/初始化GUI,所以進度是當然,不會更新,直到EDT再次空閒(即初始化完成)。

JProgressBar我已經得到使用「paintImmediately」重繪,但我似乎無法使其正常工作他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(); 

     } 
    }); 
} 

造成調用progressBar.setValue()/progressLabel.setText()上面當然會在重繪只要得到排隊的EDT正忙,導致重繪後,我們都在做,而不是沿途更新..

+1

*「大大簡化了,當然」*要​​發佈大大簡化了答覆的代碼,請發佈[SSCCE](http://pscode.org/sscce.html)(當然)。 –

回答

2

這有可能是constructSome*HeavyGUI()真的需要足夠長的關係,但它是可能填補數據模型是問題。相反,構建並顯示空的GUI元素並啓動一個或多個SwingWorker實例來封送每個元素的數據。有相關的例子herehere

附錄:如果問題是實例化組件,而不是加載數據模型,則可以按照下面的註釋中的建議將調用鏈接到invokeLater()。如果你正在實例化很多組件,請考慮flyweight patternJTable是一個熟悉的例子。

+0

它沒有填充導致問題的數據,因爲在構建過程中沒有獲取/填充數據。 –

+0

我沒見過。如果你不能卸載任何東西,你必須將調用鏈接到'invokeLater()'。例如,讓'constructHeavy1()'以'constructHeavy2()'的'invokeLater()'結尾,等等。 – trashgod

+0

是的,這可能是一種可能性,儘管是一個尷尬的,我想.. –

2

將長時間運行的代碼移動到單獨的線程中,並使用SwingUtilities.invokeAndWait或invokeLater更新GUI。

3

我會建議使用SwingWorker,那麼你可以更新的JProgressBar正確的EDT,沒有任何凍結或isuees與Concurency in Swing

有另一個選項,通過使用Runnable#thread,但然後你必須將所有輸出包裝到圖形界面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)你表現出另一個問題,爲什麼你在飛行/運行時創造了大量頂層容器中,僅創建所需數量-use通過removeAll()

2)here可能是你所需要的,在JTable所有這些JProgressBars相當入店和配置

3)這是你的paintImmediately(),真正的原因不畫任何進展到JLabel但使用JProgressBar#setValue(int); 相反,

+0

不會工作..如果你仔細閱讀我的文章,你會注意到我知道所有的Swing/EDT線程問題..我不能把長時間運行的代碼放在不同的線程上,因爲它是GUI構建代碼,它必須根據Swing規則在EDT上運行。我之後實際上是直接在我的EDT上運行時重新繪製進度對話框的方法.. –

+0

1)please don如果你能夠爲某些JComponents創建自己的用戶界面,2)你可以顯示(我的好奇心)你如何實現paintImmediately(),因爲只有在EDT存在時才能使用它總是需要測試是否(SwingUtilities.isEventDispatchThread())3)不重新發明輪子,JProgressbar可以通過實施的方法輕鬆做到這一點 – mKorbel

+0

1)好的,但我應該用什麼? 2)/ 3)請參閱我的原始文章中的編輯作爲示例。 –