2015-10-05 101 views
1

我使用.show()之前的「阻塞」代碼像一個while循環。但即使.show被調用,UI實際上也不會顯示被調用的面板。Java CardLayout .show()沒有顯示

這裏是展示問題的代碼: (警告:該代碼包含了一段真正的循環。)

import javax.swing.JFrame; 
import java.awt.CardLayout; 
import java.awt.event.ActionEvent; 

import javax.swing.JPanel; 
import javax.swing.JLabel; 
import javax.swing.JButton; 

public class CardTest extends JFrame{ 
public CardTest() { 
    CardLayout cl = new CardLayout(0,0); 
    getContentPane().setLayout(cl); 

    JPanel panelA = new JPanel(); 
    getContentPane().add(panelA, "PanelA"); 

    JLabel lblPanelA = new JLabel("Panel A"); 
    panelA.add(lblPanelA); 

    JButton btnSwitchToPanel = new JButton("Switch to Panel B"); 
    panelA.add(btnSwitchToPanel); 

    JPanel panelB = new JPanel(); 
    getContentPane().add(panelB, "PanelB"); 

    JLabel lblPanelB = new JLabel("Panel B"); 
    panelB.add(lblPanelB); 

    btnSwitchToPanel.addActionListener(new java.awt.event.ActionListener() { 
     public void actionPerformed(ActionEvent event) { 
      cl.show(getContentPane(), "PanelB"); 
      getContentPane().revalidate(); 

      // Here is the problem. Even though cl.show is called first, 
      // it still doesn't show, before the while loop has terminated. 
      int i = 0; 
      while(i < 1000000){ 
       i++; 
       System.out.println(i); 
      } 
     } 
    }); 

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    pack(); 
    setVisible(true); 
} 

public static void main(String[] args){ 
    new CardTest(); 
} 
} 

如果你想知道,我需要這一個下載器,其中同時實現真正的循環(下載文件)在按下第一個面板中的按鈕後調用。第二個面板包含進度條。但即使在下載代碼之前調用.show函數,進度面板也不會顯示。

UPDATE

我知道,把循環進入一個新的線程,解決了平局的問題,但它也介紹了其他的問題,因爲我依靠的功能順序執行的循環(下載文件後(循環),Unzipp文件,移動這些文件...)。

最好的解決方案是找到一種方法讓.show()調用在繼續循環之前真正花時間切換窗格。

+0

你需要的是這樣的:http://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/ex/ProgressBarForDataFetching.java我建立這個的方式,ProgressBar本質上註冊了一個偵聽器對於更多正在加載的數據,在更多數據被提取時更新進度,然後調用重新繪製,以便UI線程可以重新繪製它以顯示更多進度。你可以在你的代碼中使用這個類,或者用它作爲你如何實現你想要做的事的一個例子 – ControlAltDel

+0

'因爲我在循環後依賴於函數的順序執行 - 所以把所有代碼放在Thread中好。文件的複製與GUI無關。或者,如果您正在使用針對每個複製文件的消息更新GUI,則代碼需要在EDT上執行。這就是爲什麼我建議使用SwingWorker的原因,因爲您可以在結果可用時發佈結果。 – camickr

+0

@camickr這就是我最終做的。我只是想避免在一個單獨的線程中有一些部分,因爲它們都屬於一個序列。但「SwingWorker」實際上是一個好主意。我不知道它的功能。我可以使用回調函數繼續執行下一個函數並保留序列,但最終我認爲它太多了,我使用了「在新線程中刪除序列的以下部分」的解決方案。 – Haeri

回答

2

發生這種情況是因爲您正在進行EventDispatchingThread的工作。此線程也負責實際繪製GUI。

你沒有別的選擇,只能在另一個線程中工作。

如:(快速+髒)

new Thread(){ 
    @Override 
    public void run() { 
     while (...) {...} 
    } 
}.start(); 
+0

將循環放置在單獨的線程中的確行得通,但打破了依賴於循環返回的其他代碼(就像下載文件之後解壓縮一樣)。我希望有一個解決方案,可能會等待毫秒,直到平局準備好繼續進入循環。 – Haeri

+1

@Haeri您沒有任何其他選擇。在EDT上運行代碼將阻止您的用戶界面正常工作。這是如何在Java中完成的。所有提供的答案在這方面都是正確的。您必須調整其餘代碼庫,以便在第二個線程中執行下載操作,而不是試圖強制Java執行不應該做的事情,因爲這是您想要編寫代碼的方式。 – Laf

2

這是因爲重繪UI在同一個線程作爲事件處理完成,並不會發生,直到事件處理完成後(即所有事件處理方法已返回)。

最好的辦法是將「阻塞」代碼移動到可運行並在工作線程中執行它。

+0

請看看更新後的問題。 – Haeri

3

我使用.show()之前的「阻塞」代碼像一個while循環。但即使.show被調用,UI實際上也不會顯示被調用的面板。

是的,因爲你是「阻塞」負責重新繪製GUI的事件調度線程(EDT)。因此,直到代碼執行完畢後,GUI才能重新繪製。

您需要創建一個單獨的線程來執行長時間運行的任務,因此您不會阻止EDT。一種方法是使用SwingWorker。 SwingWorker將爲您創建線程,並在任務完成時通知您,以便您可以更新GUI。

閱讀Swing教程中有關Concurrency的部分以獲取更多信息和工作示例。