如何从另一个线程更新JList?

3
public class ListExample {
    public static void main(String[] args) {
       final List l=new List();
        l.init();
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                l.list.updateUI();
                System.out.println("Asim");
            }
        });
    }
}

public class List extends  JFrame{
    public DefaultListModel DLM=new DefaultListModel();
    public JList list=new JList(DLM);
    private JPanel jp=new JPanel();
    private JScrollPane sp=new JScrollPane(list);
    public void init(){
        jp.add(sp);
        super.add(jp);
        super.setVisible(true);
        super.setLayout(new FlowLayout());
        super.setSize(400,500);
        UL p=new UL();
        p.UL_1();
    }
    public void Update_list(String[] n){
         list.setListData(n);
        list.updateUI();
    }

}
public class UL extends  List{
    public void UL_1(){
       t.start();
    }
    Thread t=new Thread(new Runnable() {
        @Override
        public void run() {
            //To change body of implemented methods use File | Settings | File Templates.
            String[] n={"Asim", "saif","Khan"};
            List l=new List();
            l.list.setListData(n);
            list.updateUI();
        }
    });
}

1
请详细解释一下所面临的问题!仅仅发布代码并不能让别人了解如何解释问题的“什么”和“如何”。这会告诉提供者,您自己对手头的问题有多少了解 :-) - nIcE cOw
抱歉,我在更新我的JList时遇到了问题,这是从另一个线程中更新GUI类中的JList的程序。但是,这个例子代码对我不起作用:( - user3164210
2个回答

4

Swing是一个单线程框架,这意味着你只能在事件分派线程的上下文中修改UI元素。同样,任何长时间运行的任务或其他阻塞进程将阻止EDT处理新事件。

虽然有许多方法可以实现这一点,但可能最简单的方法是使用SwingWorker

例如...

查看 Swing中的并发 了解更多详细信息


3

你最好不要以那种不好的风格编写你的Java代码。

不要让xxxList继承JFrame,这会引起误解。 如果要使用线程更新列表,可以启动一个新线程,如下所示:

之前:不鼓励使用另一个非EDT线程,会导致错误。

import java.awt.FlowLayout;
import javax.swing.*;
/*
 * This code is bad dealing with Swing component update
 * 
 * Update a Swing component from A Non-EDT thread is not encouraged
 * may  incur error like:
 * 
 * Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
 */
public class ListExampleBad {
    public static void main(String[] args) {
       final ListFrame listFrame=new ListFrame();
        listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                //use a thread to update list data
                new Thread(new UpdateList(listFrame)).start();
                System.out.println("Asim");
            }
        });
    }
}
//list fram with JList
class ListFrame extends  JFrame{
    public ListFrame(){
        jp.add(sp);
        super.add(jp);
        super.setVisible(true);
        super.setLayout(new FlowLayout());
        super.setSize(400,500);
    }
    public synchronized void updateList(String[] n){
         list.setListData(n);
         list.updateUI();
    }
    private static final long serialVersionUID = 1L;
    private DefaultListModel<String> DLM=new DefaultListModel<String>();
    private JList<String> list=new JList<String>(DLM);
    private JPanel jp=new JPanel();
    private JScrollPane sp=new JScrollPane(list);
}
//runnable dealing with data update
class UpdateList implements Runnable {
    public UpdateList(ListFrame listFrame) {
        this.ListFrame = listFrame;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        if(SwingUtilities.isEventDispatchThread()) {
            System.out.println("updating list from Event Dispatch Thread");
        }else {
            System.out.println("updating list NOT from Event Dispatch Thread");
        }
         String[] n={"Asim", "saif","Khan"};
         ListFrame.updateList(n);
    }
    private ListFrame ListFrame;
}

谢谢留言的朋友,我已经更新了代码:

《core java》指导我们遵循以下两个原则:

1)不要在EDT线程中做耗时的工作,使用SwingWorker。

2)除了EDT之外,不要在其他线程中操作Swing组件。

此外,您可以使用SwingUtilities.isEventDispatchThread()方法检查作业是否在EDT线程中执行。

方法之一:使用SwingUtilities.invokeLater

public class ListExampleBetter {
    public static void main(String[] args) {
       final ListFrameBad listFrame=new ListFrameBad();
       listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       //use SwingUtilities.invokeLater,it's ok
       SwingUtilities.invokeLater(new UpdateList(listFrame));
    }
}

另一种方法:使用SwingWorker
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.*;
/**
 * This example illustrate updating JList from thread
 */
public class ListExample {
    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ListFrame listFrame = new ListFrame();
            listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                listFrame.setLocationRelativeTo(null);
                listFrame.setVisible(true);
            }
        });
    }
}
//frame with JList
class ListFrame extends  JFrame{
    public ListFrame(){
        super("Update JList Demo");

        //initalize data field
        dataToUpdate =new String[]{"Asim", "saif","Khan"};
        DefaultListModel<String> DLM =new DefaultListModel<String>();
        DLM.addElement("wait for update...");;
        list =new JList<String>(DLM);

         //build gui
        JPanel btnPanel = new JPanel();
        JButton btnUpdate = new JButton("Update");
        btnPanel.add(btnUpdate);
        JScrollPane sp=new JScrollPane(list);
        this.add(btnPanel,BorderLayout.NORTH);
        this.add(sp,BorderLayout.CENTER);
        this.setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);

        //deal with action
        btnUpdate.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent e) {
                // TODO Auto-generated method stub
                ListUpdater updater = new ListUpdater();
                updater.execute();
            }
        });
    }
    public synchronized void updateList(String[] n){
         list.setListData(n);
         list.updateUI();
    }
    //using Swingworker to update list
    private class ListUpdater extends SwingWorker<Void,String>{
        @Override
        public Void doInBackground() {
            for(String str :dataToUpdate ) {
                publish(str);
            }
            return null;
        }
        @Override
        public void process (List<String> datas) {
            for(String str : datas) {
                model.addElement(str);
            }
        }
        @Override
        public void done() {
            if(SwingUtilities.isEventDispatchThread()) {
                System.out.println("updating list from Event Dispatch Thread");
            }else {
                System.out.println("updating list NOT from Event Dispatch Thread");
            }
            list.setModel(model);
        }
        private DefaultListModel<String> model =new DefaultListModel<String>();
    }
    public String[] getDataToUpdate() {
        return dataToUpdate;
    }
    public void setDataToUpdate(String[] dataToUpdate) {
        this.dataToUpdate = dataToUpdate;
    }

    private static final long serialVersionUID = 1L;
    private final int DEFAULT_WIDTH = 300,DEFAULT_HEIGHT = 300;
    private JList<String> list ;
    private String[] dataToUpdate ;
}

嘿,兄弟,谢谢你的回答。我会听从你的建议 :) - user3164210
1
这仍然是错误的。你应该从事件线程更新JList,而不是使用事件线程生成_另一个_线程,然后再更新JList。只需在main中执行以下操作即可在正确的线程上进行更新:SwingUtilities.invokeLater(new UpdateList(listFrame)); - DaoWen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接