JList更新冻结显示但不冻结JFrame setTitle

时间:2013-12-28 07:10:52

标签: jframe jlist event-dispatch-thread

如果我使用大量html格式的项目更新JList,则控件将停止响应,指示器将不会更新。这是有道理的,事件线程很忙。仍然可以设置标题。这是为什么?

以下是一些(长)代码,证明了这一点:

import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.*;

public class JListTest extends JFrame {

    class TestListModel extends AbstractListModel<String> {

        private static final long serialVersionUID = JListTest.serialVersionUID;
        private boolean useHtml;
        private String[] formattedList = new String[] {};

        public int getSize() {
            return formattedList.length;
        }

        public String getElementAt(int index) {
            return formattedList[index];
        }

        public void setUseHtml(boolean useHtml) {
            this.useHtml = useHtml;
        }

        public String getNewListItem() {
            if (useHtml) {
                return "<html><div style='padding:2px"
                      + ";background-color:#EDF5F4;color:black'><div style='padding:2px;font-weight:500;'>"
                      + "Item " + (100 * Math.random())
                      + "</div>"
                      + "This will change!"
                      + "</div></html>";
            } else {
                return "Item " + (100 * Math.random());
            }
        }

        public void updateItems() {
            formattedList = new String[] {"<html><h1>Loading!</h1></html>"};
            fireContentsChanged(this, 0, 1);

            Thread buildItems = new Thread() {
                @Override
                public void run() {
                    final String[] tempList = new String[3000];
                    for (int i=0; i<tempList.length; i++) {
                        tempList[i] = getNewListItem();
                    }
                    // Just show the string bashing's done
                    try {
                        SwingUtilities.invokeAndWait(new Runnable() {
                            public void run() {
                                formattedList = new String[] {"<html><h1>Updating!</h1></html>"};
                                fireContentsChanged(TestListModel.this, 0, 1);
                            }
                        });
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // Update
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            formattedList = tempList;
                            fireContentsChanged(TestListModel.this, 0, formattedList.length);
                        }
                    });
                }
            };
            buildItems.start();
        }
    }

    protected static final long serialVersionUID = 1L;

    public JListTest() {

        JPanel controlPanel = new JPanel();
        JButton updaterControl = new JButton("Add 3000");
        final JCheckBox useHtmlControl = new JCheckBox("Use HTML");
        final TestListModel model = new TestListModel();
        JList<String> list = new JList<String>(model);
        JScrollPane scrollPane = new JScrollPane(list);
        final JLabel durationIndicator = new JLabel("0");

        controlPanel.add(useHtmlControl, BorderLayout.WEST);
        controlPanel.add(updaterControl, BorderLayout.EAST);

        getContentPane().add(controlPanel, BorderLayout.PAGE_START);
        getContentPane().add(scrollPane, BorderLayout.CENTER);
        getContentPane().add(durationIndicator, BorderLayout.PAGE_END);

        useHtmlControl.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                model.setUseHtml(useHtmlControl.isSelected());
            }
        });
        useHtmlControl.setSelected(false);

        updaterControl.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                model.updateItems();
            }
        });

        Timer counter = new Timer();
        counter.schedule(new TimerTask() {
            @Override
            public void run() {
                String previousCounter = durationIndicator.getText();
                String newCounter = Integer.toString(
                    Integer.parseInt(previousCounter) + 1);
                durationIndicator.setText(newCounter);
                setTitle(newCounter);
            }
        }, 0, 100);
    }

    public static void main(String args[]) {
        JListTest jlt = new JListTest();
        jlt.pack();
        jlt.setSize(300, 300);
        jlt.setVisible( true );
    }
}

1 个答案:

答案 0 :(得分:1)

答案非常明显 - 因为Window title不是Swing组件,它是OS本机实体。

因此,更改不必通过Swing Event Queue,但在Unix的情况下直接转到XDecoratedPeer.updateWMName,在其他操作系统中转到其他类。

更有趣的问题是如何避免UI阻塞,但我认为只有Swing才有可能,你必须实现一些延迟加载或批量渲染。