Почему я иногда получаю пустые JLists после обновления содержимого через модель списка? - PullRequest
3 голосов
/ 06 мая 2011

У меня повторяющаяся проблема, когда у меня есть JList, который я хочу обновить с новым содержимым.Я использую DefaultListModel, который предоставляет методы для добавления нового контента в список, но при использовании этих методов я обнаружил, что некоторая доля вызовов приводит к совершенно пустому 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);
    }

}

Любые указатели будут очень кстати.

Ответы [ 4 ]

8 голосов
/ 06 мая 2011

Посмотрите на этот код:

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.Работает плавно.

6 голосов
/ 06 мая 2011

Да, вы должны убедиться, что он работает на EDT. Интересно, вы не заметили никакого исключения? Я получил один при первом запуске.

Код для использования вместо этого (удалите 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();

Вот почему его можно сохранить, как это хорошо объясняется в классе 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."

4 голосов
/ 06 мая 2011

, который никогда не вызывает void updateList (...), но внутри я пропустил sleep(int), для Swing лучше и требуется использование java.swing.Timer http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html

0 голосов
/ 25 февраля 2017

При попытке реализовать вышеуказанный ответ в моей программе это не сработало, поэтому я пришел к простому методу ниже, который добавляет 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 есть глюки, связана с временным интервалом, с которым добавляются элементы, поэтому, чтобы избежать этого, время в моем коде должно быть сокращено, я использую менее 1 секунды (100) и работает нормально, задержка нижней нити может вызвать сбой

...