2011-05-06 2 views
3

새로운 내용으로 업데이트하려는 JList가있는 곳에서 반복되는 문제가 있습니다. 나는 목록에 새 내용을 추가하는 메소드를 제공하는 DefaultListModel을 사용하고 있지만 이러한 메소드를 사용하면 호출의 일부분이 완전히 빈 JList가됩니다. 업데이트 작동 여부가 무작위로 전송 된 데이터와 관련이없는 것으로 보입니다.목록 모델을 통해 내용을 업데이트 한 후 때때로 빈 JList를 가져 오는 이유는 무엇입니까?

다음은 문제를 보여주는 간단한 프로그램입니다. 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); 
    } 

} 

모든 포인터는 매우 환영받을 것입니다. 스윙에 대한 무효 updateList (...)하지만 난 sleep(int) 놓친 내부를 호출하지

답변

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

다시 수면 (정수); – mKorbel

+0

그래, 그 잠은 EDT 또는 메인 스레드를 만지지 않습니다, 그것은 스윙 워커 스레드를 일시 중지합니다. 따라서 화면이 정지되거나 화면이 비어 있지 않습니다. – dhblah

+0

+1 이것이 워크 플로우를 적절히 위임하는 방법이라고 생각합니다. – mre

4

더 나은이며 네 당신이 EDT에서 실행 확인해야

+1

그래,하지만 _ 매우 복잡합니다. 하지만 당신 말이 맞아요. 목록을 업데이트하는 것은'EDT'에서해야합니다. 타이머는 – mre

+0

+1입니다. Timer는 스윙 페인트 스레드에서 실행되므로 결과적으로 모든 실행시 UI가 올바르게 업데이트됩니다. – spot35

+0

나는 대답에 대해 @sthupahsmaht 어쨌든 +1 동의합니다. – Boro

6

java.swing.Timer http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html를 사용이 필요합니다. 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

+0

예제는 간단한 타이머를 사용했지만 실제 코드에서는 더 복잡한 방식으로 데이터를 수집하기 때문에 별도의 스레드가 필요합니다.Swing 업데이트 메커니즘과 충돌하지 않는 별도의 스레드를 만들려면 Timer와 동일한 기능이 있습니까? –

+0

@ gasan이 게시 한 SwingWorker를 찾아야하지만 Java 용 Executor + SwingWorker <1.6.022 버그 http://bugs.sun.com/view_bug.do;jsessionid=c58515ce3f702ffffffffeef2e80af23ef74?bug_id=6880336 – mKorbel

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); 
    } 

} 

는이 작업을 수행 모델에 요소를 추가하거나 JList의에서 결함이 elemets되는 timerate로 인해이 왜

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

실제 이유는 루프에서 updateList를 호출하려면 그래서 그것을 피하기 위해 시간이 내 코드에서 줄이기 위해 1 초 미만을 사용하여 오전덜 슬립 (100) 그리고 그게 잘 작동 낮은 결함 스레드가 발생할 수 있습니다 글리치