Why sometimes I get empty JList after updating the content through the list model?

I have a recurring problem when I have a JList that I want to update with new content. I am using DefaultListModel, which provides methods for adding new content to the list, but when using these methods, I found that some portion of calls result in a completely empty JList. Regardless of whether the update works, it is random and is not related to the data being sent.

Below is a simple program that demonstrates the problem. It simply generates a size list for updating the JList, but when launched, the contents of the list appear and disappear, seemingly in random order.

As far as I can tell, I am following the right API for this, but I assume that something fundamental is missing me.

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

}

Any pointers would be very welcome.

+3
source share
4 answers

Take a look at this code:

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

}

Implemented by SwingWorker. It runs smoothly.

+8
source

Yes, you have to make sure that it works on EDT. Interestingly, you did not notice any exceptions? I got one on the first run.

Code to use (remove UpdateRunner and turn it into 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();

This is why it saves on usage, as it is well explained in the class document :

"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."

+6
source

void updateList (...), sleep(int), Swing java.swing.Timer http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html

+4

, , 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 t.sleep(100), .

0

All Articles