2011-05-06 49 views
3

我有一個反覆出現的問題,我有一個JList,我希望用新內容更新。我使用了一個DefaultListModel,它提供了向列表中添加新內容的方法,但是當使用這些方法時,我發現某些比例的調用會導致完全空白的JList。更新是否工作似乎是隨機的,並且與發送的數據無關。爲什麼我在通過列表模型更新內容後有時會出現空白的JLists?

下面是一個簡單的程序,它演示了這個問題。它只是生成一個增加大小的列表來更新JList,但是當運行時,列表內容看起來隨機出現和消失。

據我可以告訴我正在遵循正確的API來做到這一點,但我想必須有一些基本的東西我想念。

import java.awt.BorderLayout; 
import javax.swing.*; 

public class ListUpdateTest extends JPanel { 

    private JList list; 
    private DefaultListModel model; 

    public ListUpdateTest() { 
     model = new DefaultListModel(); 
     list = new JList(model); 

     setLayout(new BorderLayout()); 

     add(new JScrollPane(list),BorderLayout.CENTER); 
     new UpdateRunner(); 
    } 

    public void updateList (String [] entries) { 
     model.removeAllElements(); 
     for (int i=0;i<entries.length;i++) { 
      model.addElement(entries[i]); 
     } 
    } 

    private class UpdateRunner implements Runnable { 

     public UpdateRunner() { 
      Thread t = new Thread(this); 
      t.start(); 
     } 

     public void run() { 

      while (true) { 
       int entryCount = model.size()+1; 

       System.out.println("Should be "+entryCount+" entries"); 

       String [] entries = new String [entryCount]; 

       for (int i=0;i<entries.length;i++) { 
        entries[i] = "Entry "+i; 
       } 

       updateList(entries); 

       try { 
        Thread.sleep(1000); 
       } 
       catch (InterruptedException e) {} 
      } 
     } 
    } 

    public static void main (String [] args) { 

     JDialog dialog = new JDialog(); 
     dialog.setContentPane(new ListUpdateTest()); 
     dialog.setSize(200,400); 
     dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 
     dialog.setModal(true); 
     dialog.setVisible(true); 
     System.exit(0); 
    } 

} 

任何指針都會受到歡迎。

回答

8

看看這個代碼:

import java.awt.BorderLayout; 
import javax.swing.*; 
import javax.swing.SwingWorker; 
import java.util.Arrays; 
import java.util.List; 
public class ListUpdateTest extends JPanel { 

    private JList list; 
    private DefaultListModel model; 

    public ListUpdateTest() { 
     model = new DefaultListModel(); 
     list = new JList(model); 

     setLayout(new BorderLayout()); 

     add(new JScrollPane(list),BorderLayout.CENTER); 
     (new UpdateRunner()).execute(); 
    } 

    public void updateList (List<String> entries) { 
     model.removeAllElements(); 
     for (String entry : entries) { 
      model.addElement(entry); 
     } 
    } 
    private class UpdateRunner extends SwingWorker<List<String>, List<String>>{ 

     @Override 
     public List<String> doInBackground() { 
      while (true) { 
       int entryCount = model.size()+1; 

       System.out.println("Should be "+entryCount+" entries"); 

       String [] entries = new String [entryCount]; 

       for (int i=0;i<entries.length;i++) { 
        entries[i] = "Entry "+i; 
       } 

       publish(Arrays.asList(entries)); 

       try { 
        Thread.sleep(1000); 
       } 
       catch (InterruptedException e) {} 
      } 
      return null; 
     } 
     @Override 
     protected void process(List<List<String>> entries) { 
      for (List<String> entry : entries) { 
       updateList(entry); 
      } 
     } 
     @Override 
     protected void done() { 
      updateList(Arrays.asList("done")); 
     } 
    } 

    public static void main (String [] args) { 

     JDialog dialog = new JDialog(); 
     dialog.setContentPane(new ListUpdateTest()); 
     dialog.setSize(200,400); 
     dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 
     dialog.setModal(true); 
     dialog.setVisible(true); 
     System.exit(0); 
    } 

} 

由SwingWorker實現。工作順利。

+0

但再次與睡眠(int); – mKorbel 2011-05-06 13:32:41

+0

是的,但那個睡眠不接觸EDT或主線程,它暫停了Swing Worker線程。所以沒有凍結或空的屏幕。 – dhblah 2011-05-06 13:34:16

+0

+1我相信這是如何正確委託這樣的工作流程。 – mre 2011-05-06 13:47:32

4

,從來沒有調用無效updateList(...),但裏面我錯過了sleep(int),對於Swing是更好的使用java.swing.Timer http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html

+1

是的,但在一個非常複雜的方式。但是你是對的,更新列表應該在'EDT'上完成。 – mre 2011-05-06 12:24:49

+0

+1定時器。定時器在swing重繪線程中運行,因此,在每次射擊時都會正確更新UI。 – spot35 2011-05-06 12:37:06

+0

我同意@sthupahsmaht無論如何+1作爲答案。 – Boro 2011-05-06 12:37:15

6

是啊,你應該確保它運行在美國東部時間要求。 我不知道你沒有注意到任何例外BTW? 我第一次拿到一個。

代碼,改爲使用(刪除UpdateRunner並把它變成javax.swing.Timer):

 Timer t = new Timer(1000, new ActionListener() {  
      @Override 
      public void actionPerformed(ActionEvent e) 
      { 
       int entryCount = model.size()+1;  
       System.out.println("Should be "+entryCount+" entries");  
       String [] entries = new String [entryCount];  
       for (int i=0;i<entries.length;i++) { 
        entries[i] = "Entry "+i; 
       }  
       updateList(entries); 
      } 
     }); 
     t.setRepeats(true); 
     t.start(); 

這就是爲什麼它被保存到使用它,因爲它是在the class doc很好地解釋說:

"The javax.swing.Timer has two features that can make it a little easier to use with GUIs. First, its event handling metaphor is familiar to GUI programmers and can make dealing with the event-dispatching thread a bit simpler. Second, its automatic thread sharing means that you don't have to take special steps to avoid spawning too many threads. Instead, your timer uses the same thread used to make cursors blink, tool tips appear, and so on."

+0

+1,http://download.oracle.com/javase/7/docs/api/javax/swing/Timer.html#setDelay%28int%29 – mKorbel 2011-05-06 13:08:17

+0

儘管我的示例只是使用了一個簡單的計時器,但在我的真實代碼中,我因爲它以更復雜的方式收集數據,所以需要單獨的線程來執行更新。是否有一個等價的定時器來創建一個單獨的線程不會與Swing更新機制衝突? – 2011-05-06 13:19:50

+0

您必須查找由@ gasan發佈的SwingWorker,但請注意Executor + SwingWorker for Java <1.6.022 bug http://bugs.sun.com/view_bug.do;jsessionid=c58515ce3f702ffffffffeef2e80af23ef74?bug_id=6880336 – mKorbel 2011-05-06 13:36:59

0

雖然試圖在我的程序中實現上面的答案,但它沒有工作,所以我想出了簡單的方法,以下簡單的方法添加到jList沒有小故障。

public static void updateList (String entries, DefaultListModel model) { 
    try { 
     AddElement t = new AddElement(entries, model); 
     t.sleep(100); t.stop(); 
    } catch (InterruptedException ex) { 
     Logger.getLogger(Others.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 

static class AddElement extends Thread { 

    public AddElement(String entries, DefaultListModel model) { 
     model.addElement(entries); 
    } 

} 

添加元素到模型只是這樣做,還是直接致電updateList在一個循環

int entryCount = model.size()+1; 
updateList("Entry "+entryCount, model); 

實際之所以有在的jList故障是由於在這elemets是timerate以避免它在我的代碼上面減少的時間我上面我使用不到1秒t.sleep(100)它工作正常低線程延遲可能會導致故障

相關問題