开发者

Why do I sometimes get blank JLists after updating contents through the list model?

开发者 https://www.devze.com 2023-03-03 03:01 出处:网络
I have a recurring problem where I have a JList which I wish to update with new contents.I\'m using a DefaultListModel which provides methods for adding new content to the list but when using these me

I have a recurring problem where I have a JList which I wish to update with new contents. I'm using a DefaultListModel which provides methods for adding new content to the list but when using these methods I find that some proportion of the calls result in a completely blank JList. Whether or not the update works seems to be random, and not related to the data which is sent.

Below is a simple program which demonstrates the problem. It simply generates a list of an increasing size to update a JList, but when run the list cont开发者_如何转开发ents appear and disappear seemingly at random.

As far as I can tell I'm following the correct API to do this, but I guess there must be something fundamental I'm missing.

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.


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. Works smooth.


Yea you should make sure it runs on EDT. I wonder did you not notice any exception BTW? I got one on first run.

Code to use instead (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 is save to use it, as it is nicely explained in 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.


that never call void updateList(...) but inside I missed sleep(int), for Swing is better and required using java.swing.Timer http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html


While trying to implements the above answer in my program it didn't work so i came up with the simple method bellow that add to jList with no glitch at all.

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

 }

To add an element into the model just do this or just call updateList in a loop

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

The actual reason why there is glitches in the jList is due to the timerate at which elemets is added so to avoid it the time hass to be reduced in my code above i am using less than 1 sec t.sleep(100) and it works fine lower thread delay may cause glitch

0

精彩评论

暂无评论...
验证码 换一张
取 消